/** * Actually process and submit the search * * @access public * @param bool $returnIndexErrors Should we die inside the index code if * we encounter an error (false) or return * it for access via the getIndexError() * method (true)? * @param bool $recommendations Should we process recommendations along * with the search itself? * @return object solr result structure (for now) */ public function processSearch($returnIndexErrors = false, $recommendations = false) { global $timer; global $analytics; // Our search has already been processed in init() $search = $this->searchTerms; // Build a recommendations module appropriate to the current search: if ($recommendations) { $this->initRecommendations(); } $timer->logTime("initRecommendations"); // Tag searches need to be handled differently if (count($search) == 1 && isset($search[0]['index']) && $search[0]['index'] == 'tag') { // If we managed to find some tag matches, let's override the search // array. If we didn't find any tag matches, we should return an // empty record set. $newSearch = $this->processTagSearch($search[0]['lookfor']); $timer->logTime("process Tag search"); // Save search so it displays correctly on the "no hits" page: $this->publicQuery = $search[0]['lookfor']; if (empty($newSearch)) { return array('response' => array('numFound' => 0, 'docs' => array())); } else { $search = $newSearch; } } // Build Query $query = $this->indexEngine->buildQuery($search); $timer->logTime("build query"); if (PEAR_Singleton::isError($query)) { return $query; } // Only use the query we just built if there isn't an override in place. if ($this->query == null) { $this->query = $query; } // Define Filter Query $filterQuery = $this->hiddenFilters; //Remove any empty filters if we get them //(typically happens when a subdomain has a function disabled that is enabled in the main scope) foreach ($this->filterList as $field => $filter) { if (empty($field)) { unset($this->filterList[$field]); } } foreach ($this->filterList as $field => $filter) { foreach ($filter as $value) { $analytics->addEvent('Apply Facet', $field, $value); // Special case -- allow trailing wildcards: if (substr($value, -1) == '*') { $filterQuery[] = "{$field}:{$value}"; } elseif (preg_match('/\\A\\[.*?\\sTO\\s.*?]\\z/', $value)) { $filterQuery[] = "{$field}:{$value}"; } else { if (!empty($value)) { $filterQuery[] = "{$field}:\"{$value}\""; } } } } // If we are only searching one field use the DisMax handler // for that field. If left at null let solr take care of it if (count($search) == 1 && isset($search[0]['index'])) { $this->index = $search[0]['index']; } // Build a list of facets we want from the index $facetSet = array(); if (!empty($this->facetConfig)) { $facetSet['limit'] = $this->facetLimit; foreach ($this->facetConfig as $facetField => $facetName) { $facetSet['field'][] = $facetField; } if ($this->facetOffset != null) { $facetSet['offset'] = $this->facetOffset; } if ($this->facetPrefix != null) { $facetSet['prefix'] = $this->facetPrefix; } if ($this->facetSort != null) { $facetSet['sort'] = $this->facetSort; } } $timer->logTime("create facets"); // Build our spellcheck query if ($this->spellcheck) { if ($this->spellSimple) { $this->useBasicDictionary(); } $spellcheck = $this->buildSpellingQuery(); // If the spellcheck query is purely numeric, skip it if // the appropriate setting is turned on. if ($this->spellSkipNumeric && is_numeric($spellcheck)) { $spellcheck = ""; } } else { $spellcheck = ""; } $timer->logTime("create spell check"); // Get time before the query $this->startQueryTimer(); // The "relevance" sort option is a VuFind reserved word; we need to make // this null in order to achieve the desired effect with Solr: $finalSort = $this->sort == 'relevance' ? null : $this->sort; // The first record to retrieve: // (page - 1) * limit = start $recordStart = ($this->page - 1) * $this->limit; $this->indexResult = $this->indexEngine->search($this->query, $this->index, $filterQuery, $recordStart, $this->limit, $facetSet, $spellcheck, $this->dictionary, $finalSort, $this->fields, $this->method, $returnIndexErrors); $timer->logTime("run solr search"); // Get time after the query $this->stopQueryTimer(); // How many results were there? if (!isset($this->indexResult['response']['numFound'])) { //An error occurred $this->resultsTotal = 0; } else { $this->resultsTotal = $this->indexResult['response']['numFound']; } // Process spelling suggestions if no index error resulted from the query if ($this->spellcheck && !isset($this->indexResult['error'])) { // Shingle dictionary $this->processSpelling(); // Make sure we don't endlessly loop if ($this->dictionary == 'default') { // Expand against the basic dictionary $this->basicSpelling(); } } // If extra processing is needed for recommendations, do it now: if ($recommendations && is_array($this->recommend)) { foreach ($this->recommend as $currentSet) { foreach ($currentSet as $current) { $current->process(); } } } //Add debug information to the results if available if ($this->debug) { $explainInfo = $this->indexResult['debug']['explain']; foreach ($this->indexResult['response']['docs'] as $key => $result) { if (array_key_exists($result['id'], $explainInfo)) { $result['explain'] = $explainInfo[$result['id']]; $this->indexResult['response']['docs'][$key] = $result; } } } // Return the result set return $this->indexResult; }