Ejemplo n.º 1
0
 /**
  * Execute a search.
  *
  * @param	 string	$query			The XQuery script in binary encoding.
  * @param	 string	$handler		The Query Handler to use (null for default)
  * @param	 array	$filter		 The fields and values to filter results on
  * @param	 int	  $start			The record to start with
  * @param	 int	  $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		If Solr reports a syntax error,
  *																			should we fail outright (false) or
  *																			treat it as an empty result set with
  *																			an error key set (true)?
  * @access	public
  * @throws	object							PEAR Error
  * @return	array							 An array of query results
  */
 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)
 {
     global $timer;
     // Query String Parameters
     $options = array('q' => $query, 'rows' => $limit, 'start' => $start, 'indent' => 'yes');
     //For FRBR, enable this and then update display
     //$options['group'] = 'true';
     //$options['group.field'] = 'grouping_term';
     // 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);
     }
     //Check to see if we need to automatically convert to a proper case only (no stemming search)
     //We will do this whenever all or part of a string is surrounded by quotes.
     if (preg_match('/\\".+?\\"/', $query)) {
         if ($handler == 'Keyword') {
             $handler = 'KeywordProper';
         } else {
             if ($handler == 'Subject') {
                 $handler = 'SubjectProper';
             } else {
                 if ($handler == 'AllFields') {
                     $handler = 'AllFieldsProper';
                 } else {
                     if ($handler == 'Title') {
                         $handler = 'TitleProper';
                     }
                 }
             }
         }
     }
     // Determine which handler to use
     if (!$this->isAdvanced($query)) {
         //Escape : to make sure that the query isn't treated as a field spec.
         $query = str_replace(':', '\\:', $query);
         $ss = is_null($handler) ? null : $this->_getSearchSpecs($handler);
         // 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) {
                     $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 do we need to format the query based on
             // a setting in the YAML search specs?	If $ss is an array
             // at this point, it indicates that we found YAML details.
             if (is_array($ss)) {
                 $options['q'] = $this->_buildQueryComponent($handler, $query);
             } else {
                 if (!empty($handler)) {
                     $options['q'] = "({$handler}:{$query})";
                 }
             }
         }
     } else {
         // Force boolean operators to uppercase if we are in a case-insensitive
         // mode:
         if (!$this->caseSensitiveBooleans) {
             $query = SolrUtils::capitalizeBooleans($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);
         }
     }
     $timer->logTime("build query");
     // Limit Fields
     if ($fields) {
         $options['fl'] = $fields;
     } else {
         // This should be an explicit list
         $options['fl'] = '*,score';
     }
     if (is_object($this->searchSource)) {
         $defaultFilters = preg_split('/\\r\\n/', $this->searchSource->defaultFilter);
         foreach ($defaultFilters as $tmpFilter) {
             $filter[] = $tmpFilter;
         }
     }
     //Apply automatic boosting (only to biblio and econtent queries)
     if (preg_match('/.*(biblio|econtent).*/i', $this->host)) {
         //unset($options['qt']); //Force the query to never use dismax handling
         $searchLibrary = Library::getSearchLibrary($this->searchSource);
         //Boost items owned at our location
         $searchLocation = Location::getSearchLocation($this->searchSource);
         $boostFactors = $this->getBoostFactors($searchLibrary, $searchLocation);
         if (isset($options['qt']) && $options['qt'] == 'dismax') {
             //Boost by number of holdings
             if (count($boostFactors) > 0) {
                 $options['bf'] = "sum(" . implode(',', $boostFactors) . ")";
             }
             //print ($options['bq']);
         } else {
             $baseQuery = $options['q'];
             //Boost items in our system
             if (count($boostFactors) > 0) {
                 $boost = "sum(" . implode(',', $boostFactors) . ")";
             } else {
                 $boost = '';
             }
             $options['q'] = "{!boost b={$boost}} {$baseQuery}";
             //echo ("Advanced Query " . $options['q']);
         }
         $timer->logTime("apply boosting");
         $scopingFilters = $this->getScopingFilters($searchLibrary, $searchLocation);
         $timer->logTime("apply filters based on location");
     } else {
         //Non book search (genealogy)
         $scopingFilters = array();
     }
     if ($filter != null && $scopingFilters != null) {
         if (!is_array($filter)) {
             $filter = array($filter);
         }
         $filters = array_merge($filter, $scopingFilters);
     } else {
         if ($filter == null) {
             $filters = $scopingFilters;
         } else {
             $filters = $filter;
         }
     }
     // 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']);
         if (isset($facet['field']) && is_array($facet['field']) && in_array('date_added', $facet['field'])) {
             $options['facet.date'] = 'date_added';
             $options['facet.date.end'] = 'NOW';
             $options['facet.date.start'] = 'NOW-1YEAR';
             $options['facet.date.gap'] = '+1WEEK';
             foreach ($facet['field'] as $key => $value) {
                 if ($value == 'date_added') {
                     unset($facet['field'][$key]);
                     break;
                 }
             }
         }
         if (isset($facet['field'])) {
             $options['facet.field'] = $facet['field'];
             if ($options['facet.field'] && is_array($options['facet.field'])) {
                 foreach ($options['facet.field'] as $key => $facetName) {
                     if (strpos($facetName, 'availability_toggle') === 0) {
                         $options['facet.field'][$key] = '{!ex=avail}' . $facetName;
                     }
                 }
             }
         } else {
             $options['facet.field'] = null;
         }
         unset($facet['field']);
         $options['facet.prefix'] = isset($facet['prefix']) ? $facet['prefix'] : null;
         unset($facet['prefix']);
         $options['facet.sort'] = isset($facet['sort']) ? $facet['sort'] : null;
         unset($facet['sort']);
         if (isset($facet['offset'])) {
             $options['facet.offset'] = $facet['offset'];
             unset($facet['offset']);
         }
         if ($searchLibrary && $searchLibrary->showAvailableAtAnyLocation) {
             $options['f.available_at.facet.missing'] = 'true';
         }
         foreach ($facet as $param => $value) {
             $options[$param] = $value;
         }
     }
     $timer->logTime("build facet options");
     //Check to see if there are filters we want to show all values for
     if (isset($filters) && is_array($filters)) {
         foreach ($filters as $key => $value) {
             if (strpos($value, 'availability_toggle') === 0) {
                 $filters[$key] = '{!tag=avail}' . $value;
             }
         }
     }
     // Build Filter Query
     if (is_array($filters) && count($filters)) {
         $options['fq'] = $filters;
     }
     // 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}}}}';
     }
     if ($this->debug) {
         echo '<pre>Search options: ' . print_r($options, true) . "\n";
         if ($filters) {
             echo "\nFilterQuery: ";
             foreach ($filters as $filterItem) {
                 echo " {$filterItem}";
             }
         }
         if ($sort) {
             echo "\nSort: " . $options['sort'];
         }
         echo "</pre>\n";
         $options['debugQuery'] = 'on';
     }
     $timer->logTime("end solr setup");
     $result = $this->_select($method, $options, $returnSolrError);
     $timer->logTime("run select");
     if (PEAR_Singleton::isError($result)) {
         PEAR_Singleton::raiseError($result);
     }
     return $result;
 }
Ejemplo n.º 2
0
 /**
  * Build Query string from search parameters
  *
  * @access  private
  * @param   array   $search     An array of search parameters
  * @return  string              The query
  */
 private 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 = SolrUtils::capitalizeBooleans($lookfor);
                 }
                 // Prepend the index name, unless it's the special "AllFields" index:
                 if ($index != 'AllFields') {
                     $query .= "{$index}:({$lookfor})";
                 } else {
                     $query .= "{$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 : '';
 }