/** * Query parsing helper routine. * Returned structure is based on that used by the Search::QueryParser Perl module. */ function _parseQuery($signTokens, $tokens, &$pos, $total) { $return = array('+' => array(), '' => array(), '-' => array()); $postBool = $preBool = ''; $notOperator = String::strtolower(Locale::translate('search.operator.not')); $andOperator = String::strtolower(Locale::translate('search.operator.and')); $orOperator = String::strtolower(Locale::translate('search.operator.or')); while ($pos < $total) { if (!empty($signTokens[$pos])) { $sign = $signTokens[$pos]; } else { if (empty($sign)) { $sign = '+'; } } $token = String::strtolower($tokens[$pos++]); switch ($token) { case $notOperator: $sign = '-'; break; case ')': return $return; case '(': $token = ArticleSearch::_parseQuery($signTokens, $tokens, $pos, $total); default: $postBool = ''; if ($pos < $total) { $peek = String::strtolower($tokens[$pos]); if ($peek == $orOperator) { $postBool = 'or'; $pos++; } else { if ($peek == $andOperator) { $postBool = 'and'; $pos++; } } } $bool = empty($postBool) ? $preBool : $postBool; $preBool = $postBool; if ($bool == 'or') { $sign = ''; } if (is_array($token)) { $k = $token; } else { $k = ArticleSearchIndex::filterKeywords($token, true); } if (!empty($k)) { $return[$sign][] = $k; } $sign = ''; break; } } return $return; }
/** * Return an array of search results matching the supplied * keyword IDs in decreasing order of match quality. * Keywords are supplied in an array of the following format: * $keywords[ARTICLE_SEARCH_AUTHOR] = array('John', 'Doe'); * $keywords[ARTICLE_SEARCH_...] = array(...); * $keywords[null] = array('Matches', 'All', 'Fields'); * @param $journal object The journal to search * @param $keywords array List of keywords * @param $error string a reference to a variable that will * contain an error message if the search service produces * an error. * @param $publishedFrom object Search-from date * @param $publishedTo object Search-to date * @param $rangeInfo Information on the range of results to return * @return VirtualArrayIterator An iterator with one entry per retrieved * article containing the article, published article, issue, journal, etc. */ function &retrieveResults(&$journal, &$keywords, &$error, $publishedFrom = null, $publishedTo = null, $rangeInfo = null) { // Pagination if ($rangeInfo && $rangeInfo->isValid()) { $page = $rangeInfo->getPage(); $itemsPerPage = $rangeInfo->getCount(); } else { $page = 1; $itemsPerPage = ARTICLE_SEARCH_DEFAULT_RESULT_LIMIT; } // Check whether a search plug-in jumps in to provide ranked search results. $totalResults = null; $results =& HookRegistry::call('ArticleSearch::retrieveResults', array(&$journal, &$keywords, $publishedFrom, $publishedTo, $page, $itemsPerPage, &$totalResults, &$error)); // If no search plug-in is activated then fall back to the // default database search implementation. if ($results === false) { // Parse the query. foreach ($keywords as $searchType => $query) { $keywords[$searchType] = ArticleSearch::_parseQuery($query); } // Fetch all the results from all the keywords into one array // (mergedResults), where mergedResults[article_id] // = sum of all the occurences for all keywords associated with // that article ID. $mergedResults =& ArticleSearch::_getMergedArray($journal, $keywords, $publishedFrom, $publishedTo); // Convert mergedResults into an array (frequencyIndicator => // $articleId). // The frequencyIndicator is a synthetically-generated number, // where higher is better, indicating the quality of the match. // It is generated here in such a manner that matches with // identical frequency do not collide. $results =& ArticleSearch::_getSparseArray($mergedResults); $totalResults = count($results); // Use only the results for the specified page. $offset = $itemsPerPage * ($page - 1); $length = max($totalResults - $offset, 0); $length = min($itemsPerPage, $length); if ($length == 0) { $results = array(); } else { $results = array_slice($results, $offset, $length); } } // Take the range of results and retrieve the Article, Journal, // and associated objects. $results =& ArticleSearch::formatResults($results); // Return the appropriate iterator. import('lib.pkp.classes.core.VirtualArrayIterator'); $returner = new VirtualArrayIterator($results, $totalResults, $page, $itemsPerPage); return $returner; }