コード例 #1
0
 /**
  * Test parseRange functionality.
  *
  * @return void
  * @access public
  */
 public function testParseRange()
 {
     // basic range test:
     $result = VuFindSolrUtils::parseRange("[1 TO 100]");
     $this->assertEquals('1', $result['from']);
     $this->assertEquals('100', $result['to']);
     // test whitespace handling:
     $result = VuFindSolrUtils::parseRange("[1      TO     100]");
     $this->assertEquals('1', $result['from']);
     $this->assertEquals('100', $result['to']);
     // test invalid ranges:
     $this->assertFalse(VuFindSolrUtils::parseRange('1 TO 100'));
     $this->assertFalse(VuFindSolrUtils::parseRange('[not a range to me]'));
 }
コード例 #2
0
ファイル: JSON_RangeVis.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Support method for getVisData() -- extract details from applied filters.
  *
  * @param array $filters Current filter list
  *
  * @return array
  * @access private
  */
 private function _processDateFacets($filters)
 {
     $result = array();
     foreach ($this->_dateFacets as $current) {
         $from = $to = '';
         if (isset($filters[$current])) {
             foreach ($filters[$current] as $filter) {
                 if ($range = VuFindSolrUtils::parseRange($filter)) {
                     $from = $range['from'] == '*' ? '' : $range['from'];
                     $to = $range['to'] == '*' ? '' : $range['to'];
                     break;
                 }
             }
         }
         $result[$current] = array($from, $to);
     }
     return $result;
 }
コード例 #3
0
ファイル: PubDateVisAjax.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Support method for getVisData() -- extract details from applied filters.
  *
  * @param array $filters Current filter list
  *
  * @return array
  * @access protected
  */
 protected function processDateFacets($filters)
 {
     $result = array();
     foreach ($this->dateFacets as $current) {
         $from = $to = '';
         if (isset($filters[$current])) {
             foreach ($filters[$current] as $filter) {
                 if ($range = VuFindSolrUtils::parseRange($filter)) {
                     $from = $range['from'] == '*' ? '' : $range['from'];
                     $to = $range['to'] == '*' ? '' : $range['to'];
                     break;
                 }
             }
         }
         $result[$current] = array($from, $to);
         $result[$current]['label'] = $this->searchObject->getFacetLabel($current);
     }
     return $result;
 }
コード例 #4
0
ファイル: Advanced.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Get the current settings for the date range facet, if it is set:
  *
  * @param object $savedSearch Saved search object (false if none)
  *
  * @return array              Date range: Key 0 = from, Key 1 = to.
  * @access private
  */
 private function _getDateRangeSettings($savedSearch = false)
 {
     // Default to blank strings:
     $from = $to = '';
     // Check to see if there is an existing range in the search object:
     if ($savedSearch) {
         $filters = $savedSearch->getFilters();
         if (isset($filters['main_date_str'])) {
             foreach ($filters['main_date_str'] as $current) {
                 if ($range = VuFindSolrUtils::parseRange($current)) {
                     $from = $range['from'] == '*' ? '' : $range['from'];
                     $to = $range['to'] == '*' ? '' : $range['to'];
                     $savedSearch->removeFilter('main_date_str:' . $current);
                     break;
                 }
             }
         }
     }
     // Send back the settings:
     return array($from, $to);
 }
コード例 #5
0
ファイル: Solr.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Returns the stored list of facets for the last search
  *
  * @param array $filter         Array of field => on-screen description listing
  * all of the desired facet fields; set to null to get all configured values.
  * @param bool  $expandingLinks If true, we will include expanding URLs (i.e.
  * get all matches for a facet, not just a limit to the current search) in the
  * return array.
  *
  * @return array                Facets data arrays
  * @access public
  */
 public function getFacetList($filter = null, $expandingLinks = false)
 {
     // If there is no filter, we'll use all facets as the filter:
     if (is_null($filter)) {
         $filter = $this->facetConfig;
     }
     // Start building the facet list:
     $list = array();
     // If we have no facets to process, give up now
     if (!isset($this->indexResult['facet_counts']['facet_fields']) && !isset($this->indexResult['facet_counts']['facet_queries']) || !is_array($this->indexResult['facet_counts']['facet_fields']) && !is_array($this->indexResult['facet_counts']['facet_queries'])) {
         return $list;
     }
     $translationPrefix = isset($this->facetTranslationPrefix) ? $this->facetTranslationPrefix : '';
     // Loop through every field returned by the result set
     $validFields = array_keys($filter);
     foreach ($this->indexResult['facet_counts']['facet_fields'] as $field => $data) {
         // Skip filtered fields and empty arrays:
         if (!in_array($field, $validFields) || count($data) < 1) {
             continue;
         }
         // Initialize the settings for the current field
         $list[$field] = array();
         // Add the on-screen label
         $list[$field]['label'] = $filter[$field];
         // This tells us whether there is a selection made in the facet group
         // Useful so we will not have to loop through the list later on
         $list[$field]['isApplied'] = false;
         // Build our array of values for this field
         $list[$field]['list'] = array();
         // Should we translate values for the current facet?
         $translate = in_array($field, $this->translatedFacets);
         // Hierarchical facets
         $hierarchical = $this->getFacetSetting('SpecialFacets', 'hierarchical');
         // Loop through values:
         foreach ($data as $facet) {
             // Initialize the array of data about the current facet:
             $currentSettings = array();
             if ($translate) {
                 if (is_array($hierarchical) && in_array($field, $hierarchical)) {
                     $facetValue = $facet[0];
                     // Remove trailing slash
                     $facetValue = rtrim($facetValue, '/');
                     $translatedValue = translate(array('prefix' => $translationPrefix, 'text' => $facetValue));
                     if ($translatedValue == $facetValue) {
                         // Didn't find a translation, so let's just clean up the display string a bit
                         $translatedValue = end(explode('/', $facetValue));
                     }
                     $currentSettings['value'] = $translatedValue;
                 } else {
                     $currentSettings['value'] = translate(array('prefix' => $translationPrefix, 'text' => $facet[0]));
                 }
             } else {
                 $currentSettings['value'] = $facet[0];
             }
             $currentSettings['untranslated'] = $facet[0];
             $currentSettings['count'] = $facet[1];
             $currentSettings['isApplied'] = false;
             $currentSettings['url'] = $this->renderLinkWithFilter("{$field}:" . $facet[0]);
             // If we want to have expanding links (all values matching the
             // facet) in addition to limiting links (filter current search
             // with facet), do some extra work:
             if ($expandingLinks) {
                 $currentSettings['expandUrl'] = $this->getExpandingFacetLink($field, $facet[0]);
             }
             // Is this field a current filter?
             // preg_replace removes the filter exclude if any
             $rawField = preg_replace('/{!ex=.+}/', '', $field);
             if (in_array($rawField, array_keys($this->filterList))) {
                 // and is this value a selected filter?
                 if (in_array($facet[0], $this->filterList[$rawField])) {
                     $currentSettings['isApplied'] = true;
                     $list[$field]['isApplied'] = true;
                 }
             }
             // Store the collected values:
             $list[$field]['list'][] = $currentSettings;
         }
     }
     foreach ($this->indexResult['facet_counts']['facet_queries'] as $key => $count) {
         list($field, $query) = explode(':', $key, 2);
         if (!in_array($field, $validFields)) {
             continue;
         }
         // Initialize the settings for the current field
         if (!isset($list[$field])) {
             $list[$field] = array();
             // Add the on-screen label
             $list[$field]['label'] = $this->pseudoFacets[$field];
             // Build our array of values for this field
             $list[$field]['list'] = array();
         }
         // Initialize the array of data about the current facet:
         $currentSettings = array();
         $range = VuFindSolrUtils::parseRange($query);
         $currentSettings['value'] = $currentSettings['untranslated'] = $query;
         $currentSettings['count'] = $count;
         $currentSettings['isApplied'] = false;
         $filter = $this->buildDateRangeFilter($field, $range['from'], $range['to']);
         $currentSettings['url'] = $this->renderLinkWithFilter($filter);
         // If we want to have expanding links (all values matching the
         // facet) in addition to limiting links (filter current search
         // with facet), do some extra work:
         if ($expandingLinks) {
             $currentSettings['expandUrl'] = $this->getExpandingFacetLink($field, $facet[0]);
         }
         // Is this field a current filter?
         if (in_array($field, array_keys($this->filterList))) {
             // and is this value a selected filter?
             if (in_array($facet[0], $this->filterList[$field])) {
                 $currentSettings['isApplied'] = true;
             }
         }
         // Store the collected values:
         $list[$field]['list'][] = $currentSettings;
     }
     // Sort configured facets alphabetically
     $alphaSorted = $this->getFacetSetting('Results_Settings', 'hierarchicalFacetSortOptions');
     if (is_array($alphaSorted)) {
         foreach ($alphaSorted as $alphaFacet => $mode) {
             if (isset($list[$alphaFacet])) {
                 if ($mode == 'all' || $mode == 'top' && isset($list[$alphaFacet]['list'][0]['untranslated'][0]) && $list[$alphaFacet]['list'][0]['untranslated'][0] == '0') {
                     usort($list[$alphaFacet]['list'], function ($a, $b) {
                         return strtolower($a['value']) > strtolower($b['value']);
                     });
                 }
             }
         }
     }
     return $list;
 }
コード例 #6
0
ファイル: MetaLib.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Build Query string from search parameters
  *
  * @param array $search An array of search parameters
  *
  * @return string       The query
  * @access protected
  */
 protected function buildQuery($search)
 {
     $groups = array();
     $excludes = array();
     if (is_array($search)) {
         $query = '';
         foreach ($search as $params) {
             // Advanced Search
             if (isset($params['group'])) {
                 $thisGroup = array();
                 // Process each search group
                 foreach ($params['group'] as $group) {
                     // Build this group individually as a basic search
                     $thisGroup[] = $this->buildQuery(array($group));
                 }
                 // Is this an exclusion (NOT) group or a normal group?
                 if ($params['group'][0]['bool'] == 'NOT') {
                     $excludes[] = join(" OR ", $thisGroup);
                 } else {
                     $groups[] = join(" " . $params['group'][0]['bool'] . " ", $thisGroup);
                 }
             }
             // Basic Search
             if (isset($params['lookfor']) && $params['lookfor'] != '') {
                 // Clean and validate input -- note that index may be in a
                 // different field depending on whether this is a basic or
                 // advanced search.
                 $lookfor = $params['lookfor'];
                 if (isset($params['field'])) {
                     $index = $params['field'];
                 } else {
                     if (isset($params['index'])) {
                         $index = $params['index'];
                     } else {
                         $index = 'AllFields';
                     }
                 }
                 // Force boolean operators to uppercase if we are in a
                 // case-insensitive mode:
                 if (!$this->caseSensitiveBooleans) {
                     $lookfor = VuFindSolrUtils::capitalizeBooleans($lookfor);
                 }
                 // Prepend the index name, unless it's the special "AllFields"
                 // index:
                 if ($index != 'AllFields') {
                     $query .= "{$index}=({$lookfor})";
                 } else {
                     $query .= "WRD=({$lookfor})";
                 }
             }
         }
     }
     // Put our advanced search together
     if (count($groups) > 0) {
         $query = "(" . join(") " . $search[0]['join'] . " (", $groups) . ")";
     }
     // and concatenate exclusion after that
     if (count($excludes) > 0) {
         $query .= " NOT ((" . join(") OR (", $excludes) . "))";
     }
     // Ensure we have a valid query to this point
     return isset($query) ? $query : '';
 }
コード例 #7
0
 /**
  * Support method for getVisData() -- extract details from applied filters.
  *
  * @param array $filters Current filter list
  *
  * @return array
  * @access protected
  */
 protected function processDateFacets($filters)
 {
     $result = array();
     $from = $to = '';
     if (isset($filters[$this->filterField])) {
         foreach ($filters[$this->filterField] as $filter) {
             if ($range = VuFindSolrUtils::parseSpatialDateRange($filter, $this->searchObject->getSpatialDateRangeFilterType())) {
                 $startDate = new DateTime("@{$range['from']}");
                 $endDate = new DateTime("@{$range['to']}");
                 $from = $startDate->format('Y');
                 $to = $endDate->format('Y');
                 break;
             }
         }
     }
     $result[$this->filterField] = array($from, $to);
     $result[$this->filterField]['label'] = $this->searchObject->getFacetLabel($this->filterField);
     return $result;
 }
コード例 #8
0
 /**
  * Support function to get publication date range. Return string in the form
  * "YYYY-YYYY"
  *
  * @param string $field Name of filter field to check for date limits.
  *
  * @return string
  * @access protected
  */
 protected function getPublishedDates($field)
 {
     // Try to extract range details from request parameters or SearchObject:
     if (isset($_REQUEST[$field . 'from']) && isset($_REQUEST[$field . 'to'])) {
         $range = array('from' => $_REQUEST[$field . 'from'], 'to' => $_REQUEST[$field . 'to']);
     } else {
         if (is_object($this->_searchObject)) {
             $currentFilters = $this->_searchObject->getFilters();
             if (isset($currentFilters[$field][0])) {
                 $range = VuFindSolrUtils::parseRange($currentFilters[$field][0]);
             }
         }
     }
     // Normalize range if we found one:
     if (isset($range)) {
         if (empty($range['from']) || $range['from'] == '*') {
             $range['from'] = 0;
         }
         if (empty($range['to']) || $range['to'] == '*') {
             $range['to'] = date('Y') + 1;
         }
         return $range['from'] . '-' . $range['to'];
     }
     // No range found?  Return empty string:
     return '';
 }
コード例 #9
0
ファイル: Solr.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Execute a search.
  *
  * @param string $query           The search query
  * @param string $handler         The Query Handler to use (null for default)
  * @param array  $filter          The fields and values to filter results on
  * @param string $start           The record to start with
  * @param string $limit           The amount of records to return
  * @param array  $facet           An array of faceting options
  * @param string $spell           Phrase to spell check
  * @param string $dictionary      Spell check dictionary to use
  * @param string $sort            Field name to use for sorting
  * @param string $fields          A list of fields to be returned
  * @param string $method          Method to use for sending request (GET/POST)
  * @param bool   $returnSolrError Fail outright on syntax error (false) or
  * treat it as an empty result set with an error key set (true)?
  *
  * @throws object                 PEAR Error
  * @return array                  An array of query results
  * @access public
  */
 public function search($query, $handler = null, $filter = null, $start = 0, $limit = 20, $facet = null, $spell = '', $dictionary = null, $sort = null, $fields = null, $method = HTTP_REQUEST_METHOD_POST, $returnSolrError = false)
 {
     // Query String Parameters
     $options = array('q' => $query, 'rows' => $limit, 'start' => $start, 'indent' => 'yes');
     // Force sort by title_sort for empty search sorted by score
     if ($limit > 0 && $query == '*:*' && (empty($sort) || $sort == 'score desc')) {
         $sort = 'title asc';
     }
     // Add Sorting
     if ($sort && !empty($sort)) {
         // There may be multiple sort options (ranked, with tie-breakers);
         // process each individually, then assemble them back together again:
         $sortParts = explode(',', $sort);
         for ($x = 0; $x < count($sortParts); $x++) {
             $sortParts[$x] = $this->_normalizeSort($sortParts[$x]);
         }
         $options['sort'] = implode(',', $sortParts);
     }
     // Determine which handler to use
     if (!$this->isAdvanced($query)) {
         $ss = is_null($handler) ? null : $this->_getSearchSpecs($handler, $query);
         // Is this a Dismax search?
         if (isset($ss['DismaxFields'])) {
             // Specify the fields to do a Dismax search on:
             $options['qf'] = implode(' ', $ss['DismaxFields']);
             // Specify the default dismax search handler so we can use any
             // global settings defined by the user:
             $options['qt'] = 'dismax';
             // Load any custom Dismax parameters from the YAML search spec file:
             if (isset($ss['DismaxParams']) && is_array($ss['DismaxParams'])) {
                 foreach ($ss['DismaxParams'] as $current) {
                     // The way we process the current parameter depends on
                     // whether or not we have previously encountered it.  If
                     // we have multiple values for the same parameter, we need
                     // to turn its entry in the $options array into a subarray;
                     // otherwise, one-off parameters can be safely represented
                     // as single values.
                     if (isset($options[$current[0]])) {
                         if (!is_array($options[$current[0]])) {
                             $options[$current[0]] = array($options[$current[0]]);
                         }
                         $options[$current[0]][] = $current[1];
                     } else {
                         $options[$current[0]] = $current[1];
                     }
                 }
             }
             // Apply search-specific filters if necessary:
             if (isset($ss['FilterQuery'])) {
                 if (is_array($filter)) {
                     $filter[] = $ss['FilterQuery'];
                 } else {
                     $filter = array($ss['FilterQuery']);
                 }
             }
         } else {
             // Not DisMax... but if we have a handler set, we may still need
             // to build a query using a setting in the YAML search specs or a
             // simple field name:
             if (!empty($handler)) {
                 $options['q'] = $this->_buildQueryComponent($handler, $query);
             }
         }
     } else {
         // Force boolean operators to uppercase if we are in a case-insensitive
         // mode:
         if (!$this->_caseSensitiveBooleans) {
             $query = VuFindSolrUtils::capitalizeBooleans($query);
         }
         // Adjust range operators if we are in a case-insensitive mode:
         if (!$this->_caseSensitiveRanges) {
             $query = VuFindSolrUtils::capitalizeRanges($query);
         }
         // Process advanced search -- if a handler was specified, let's see
         // if we can adapt the search to work with the appropriate fields.
         if (!empty($handler)) {
             $options['q'] = $this->_buildAdvancedQuery($handler, $query);
             // If highlighting is enabled, we only want to use the inner query
             // for highlighting; anything added outside of this is a boost and
             // should be ignored for highlighting purposes!
             if ($this->_highlight) {
                 $options['hl.q'] = $this->_buildAdvancedInnerQuery($handler, $query);
             }
         }
     }
     // Limit Fields
     if ($fields) {
         $options['fl'] = $fields;
     } else {
         // This should be an explicit list
         $options['fl'] = '*,score';
     }
     // Build Facet Options
     if ($facet && !empty($facet['field'])) {
         $options['facet'] = 'true';
         $options['facet.mincount'] = 1;
         $options['facet.limit'] = isset($facet['limit']) ? $facet['limit'] : null;
         unset($facet['limit']);
         $options['facet.field'] = isset($facet['field']) ? $facet['field'] : null;
         unset($facet['field']);
         if (isset($facet['prefix'])) {
             if (is_array($facet['prefix'])) {
                 foreach ($facet['prefix'] as $name => $prefix) {
                     $options["f.{$name}.facet.prefix"] = $prefix;
                     // TODO: This is a kludge, maybe something better is
                     // needed to indicate when we want more than the default
                     // limit for hierarchical facets.
                     $options["f.{$name}.facet.limit"] = 1000;
                 }
             } else {
                 $options['facet.prefix'] = $facet['prefix'];
             }
         }
         unset($facet['prefix']);
         $options['facet.sort'] = isset($facet['sort']) ? $facet['sort'] : 'count';
         unset($facet['sort']);
         if (isset($facet['offset'])) {
             $options['facet.offset'] = $facet['offset'];
             unset($facet['offset']);
         }
         if (isset($facet['query'])) {
             $options['facet.query'] = $facet['query'];
             unset($facet['query']);
         }
         foreach ($facet as $param => $value) {
             $options[$param] = $value;
         }
     }
     // Don't use the filters for an id query
     if ($handler != 'ids') {
         if ($this->_mergedRecords) {
             // Filter out merged children by default
             if (!isset($filter)) {
                 $filter = array();
             }
             $filter[] = '-merged_child_boolean:TRUE';
         } elseif ($this->_mergedRecords !== null) {
             // Filter out merged records by default
             if (!isset($filter)) {
                 $filter = array();
             }
             $filter[] = '-merged_boolean:TRUE';
         }
         if ($this->_hideComponentParts) {
             // Filter out component parts by default
             if (!isset($filter)) {
                 $filter = array();
             }
             $filter[] = '-hidden_component_boolean:TRUE';
         }
     }
     // Build Filter Query
     $this->_mergeBuildingPriority = array();
     if (is_array($filter) && count($filter)) {
         // Change 'online_boolean' filter to 'online_str_mv'
         // if sources contain merged records (i.e. if deduplication is enabled).
         if (($pos = array_search('online_boolean:"1"', $filter)) !== false) {
             $searchSettings = getExtraConfigArray('searches');
             if (isset($searchSettings['Records']['merged_records']) && $searchSettings['Records']['merged_records']) {
                 if (isset($searchSettings['Records']['sources']) && ($sources = $searchSettings['Records']['sources']) !== '') {
                     $tmp = array();
                     foreach (explode(',', $sources) as $source) {
                         $tmp[] = "\"{$source}\"";
                     }
                     $filter[$pos] = 'online_str_mv:(' . implode(' OR ', $tmp) . ')';
                 } else {
                     $filter[$pos] = 'online_str_mv:*';
                 }
             }
         }
         $options['fq'] = $filter;
         foreach ($filter as $f) {
             if (strncmp($f, 'building:', 9) == 0) {
                 // Assume we have a facet hierarchy...
                 $fullHierarchy = substr($f, 12, -1);
                 foreach (explode('/', $fullHierarchy) as $part) {
                     $this->_mergeBuildingPriority[] = $part;
                 }
             }
         }
     }
     // Enable Spell Checking
     if ($spell != '') {
         $options['spellcheck'] = 'true';
         $options['spellcheck.q'] = $spell;
         if ($dictionary != null) {
             $options['spellcheck.dictionary'] = $dictionary;
         }
     }
     // Enable highlighting
     if ($this->_highlight) {
         $options['hl'] = 'true';
         $options['hl.fl'] = '*';
         $options['hl.simple.pre'] = '{{{{START_HILITE}}}}';
         $options['hl.simple.post'] = '{{{{END_HILITE}}}}';
     }
     // Hack to make it possible to see the search debug information
     if (isset($_REQUEST['debugSolrQuery'])) {
         $options['debugQuery'] = 'true';
     }
     if ($this->debug) {
         echo '<pre>Search options: ' . print_r($options, true) . "\n";
         if ($filter) {
             echo "\nFilterQuery: ";
             foreach ($filter as $filterItem) {
                 echo " {$filterItem}";
             }
         }
         if ($sort) {
             echo "\nSort: " . $options['sort'];
         }
         echo "</pre>\n";
     }
     $result = $this->_select($method, $options, $returnSolrError);
     if (PEAR::isError($result)) {
         PEAR::raiseError($result);
     }
     return $result;
 }
コード例 #10
0
ファイル: Base.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Convert spatial date range filter to displayable string
  *
  * @param string $filter Spatial date range filter
  *
  * @return string Resulting display string
  */
 protected function spatialDateRangeFilterToString($filter)
 {
     $range = VuFindSolrUtils::parseSpatialDateRange($filter, $this->spatialDateRangeFilterType);
     if ($range === false) {
         return $filter;
     }
     $startDate = new DateTime("@{$range['from']}");
     $endDate = new DateTime("@{$range['to']}");
     if ($startDate->format('m') == 1 && $startDate->format('d') == 1 && $endDate->format('m') == 12 && $endDate->format('d') == 31) {
         return $startDate->format('Y') . ' - ' . $endDate->format('Y');
     }
     $date = new VuFindDate();
     return $date->convertToDisplayDate('U', $range['from']) . ' - ' . $date->convertToDisplayDate('U', $range['to']);
 }
コード例 #11
0
ファイル: Summon.php プロジェクト: bharatm/NDL-VuFind
 /**
  * Execute a search.
  *
  * @param array  $query      The search terms from the Search Object
  * @param array  $filterList The fields and values to filter results on
  * @param string $start      The record to start with
  * @param string $limit      The amount of records to return
  * @param string $sortBy     The value to be used by for sorting
  * @param array  $facets     The facets to include (null for defaults)
  * @param bool   $returnErr  On fatal error, should we fail outright (false) or
  * treat it as an empty result set with an error key set (true)?
  *
  * @throws object            PEAR Error
  * @return array             An array of query results
  * @access public
  */
 public function query($query, $filterList = null, $start = 1, $limit = 20, $sortBy = null, $facets = null, $returnErr = false)
 {
     // Query String Parameters
     $options = array('s.q' => $this->_buildQuery($query));
     // TODO: add configurable authentication mechanisms to identify authorized
     // users and switch this to "authenticated" when appropriate (VUFIND-475):
     $options['s.role'] = 'none';
     // Which facets should we include in results?  Set defaults if not provided.
     if (!$facets) {
         $facets = array_keys($this->_config['Facets']);
     }
     // Default to "holdings only" unless a different setting is found in the
     // filters:
     $options['s.ho'] = 'true';
     // Which filters should be applied to our query?
     $options['s.fvf'] = array();
     $options['s.rf'] = array();
     if (!empty($filterList)) {
         // Loop through all filters and add appropriate values to request:
         foreach ($filterList as $filterArray) {
             foreach ($filterArray as $filter) {
                 $safeValue = $this->_escapeParam($filter['value']);
                 // Special case -- "holdings only" is a separate parameter from
                 // other facets.
                 if ($filter['field'] == 'holdingsOnly') {
                     $options['s.ho'] = $safeValue;
                 } else {
                     if ($filter['field'] == 'excludeNewspapers') {
                         // Special case -- support a checkbox for excluding
                         // newspapers:
                         $options['s.fvf'][] = "ContentType,Newspaper Article,true";
                     } else {
                         if ($range = VuFindSolrUtils::parseRange($filter['value'])) {
                             // Special case -- range query (translate [x TO y] syntax):
                             $from = $this->_escapeParam($range['from']);
                             $to = $this->_escapeParam($range['to']);
                             $options['s.rf'][] = "{$filter['field']},{$from}:{$to}";
                         } else {
                             // Standard case:
                             $options['s.fvf'][] = "{$filter['field']},{$safeValue}";
                         }
                     }
                 }
             }
         }
     }
     // Special case -- if user filtered down to newspapers AND excluded them,
     // we can't possibly have any results:
     if (in_array('ContentType,Newspaper Article,true', $options['s.fvf']) && in_array('ContentType,Newspaper Article', $options['s.fvf'])) {
         return array('recordCount' => 0, 'documents' => array());
     }
     if (is_array($facets)) {
         $options['s.ff'] = array();
         foreach ($facets as $facet) {
             // See if parameters are included as part of the facet name;
             // if not, override them with defaults.
             $parts = explode(',', $facet);
             $facetName = $parts[0];
             $facetMode = isset($parts[1]) ? $parts[1] : 'and';
             $facetPage = isset($parts[2]) ? $parts[2] : 1;
             if (isset($parts[3])) {
                 $facetLimit = $parts[3];
             } else {
                 $facetLimit = isset($this->_config['Facet_Settings']['facet_limit']) ? $this->_config['Facet_Settings']['facet_limit'] : 30;
             }
             $facetParams = "{$facetMode},{$facetPage},{$facetLimit}";
             // Special case -- we can't actually facet on PublicationDate,
             // but we need it in the results to display range controls.  If
             // we encounter this field, set a flag indicating that we need
             // to inject it into the results for proper display later:
             if ($facetName == 'PublicationDate') {
                 $injectPubDate = true;
             } else {
                 $options['s.ff'][] = "{$facetName},{$facetParams}";
             }
         }
     }
     if (isset($sortBy)) {
         $options['s.sort'] = $sortBy;
     }
     $options['s.ps'] = $limit;
     $options['s.pn'] = $start;
     // Define Highlighting
     if ($this->_highlight) {
         $options['s.hl'] = 'true';
         $options['s.hs'] = '{{{{START_HILITE}}}}';
         $options['s.he'] = '{{{{END_HILITE}}}}';
     } else {
         $options['s.hl'] = 'false';
         $options['s.hs'] = $options['s.he'] = '';
     }
     if ($this->debug) {
         echo '<pre>Query: ';
         print_r($options);
         echo "</pre>\n";
     }
     $result = $this->_call($options);
     if (PEAR::isError($result)) {
         if ($returnErr) {
             return array('recordCount' => 0, 'documents' => array(), 'errors' => $result->getMessage());
         } else {
             PEAR::raiseError($result);
         }
     }
     // Add a fake "PublicationDate" facet if flagged earlier; this is necessary
     // in order to display the date range facet control in the interface.
     if (isset($injectPubDate) && $injectPubDate) {
         $result['facetFields'][] = array('fieldName' => 'PublicationDate', 'displayName' => 'PublicationDate', 'counts' => array());
     }
     return $result;
 }