/**
     * Queries the NateGoSearch index with a set of keywords
     *
     * Querying does not directly return a set of results. This is due to the
     * way NateGoSearch is designed. The document ids from this search are
     * stored in a results table and accessed through a unique identifier.
     *
     * @param string $keywords the search string to query.
     *
     * @return NateGoSearchResult an object containing result information.
     *
     * @see NateGoSearchResult::getUniqueId()
     */
    public function query($keywords)
    {
        static $unique_counter = 0;
        $id = sha1(uniqid($unique_counter, true));
        $keywords = $this->normalizeKeywordsForSpelling($keywords);
        if ($this->spell_checker === null) {
            $misspellings = array();
        } else {
            $misspellings = $this->spell_checker->getMisspellingsInPhrase($keywords);
        }
        $misspellings = $this->getPopularReplacements($keywords, $misspellings);
        $keywords = $this->normalizeKeywordsForSearching($keywords);
        $keywords_hash = sha1($keywords);
        $results = new NateGoSearchResult($this->db, $id, $keywords, $this->document_types);
        $results->addMisspellings($misspellings);
        $searched_keywords = array();
        $keyword = strtok($keywords, ' ');
        while ($keyword) {
            if (in_array($keyword, $this->blocked_words)) {
                $results->addBlockedWords($keyword);
            } else {
                $searched_keywords[] = NateGoSearchIndexer::stemKeyword($keyword);
                $results->addSearchedWords($keyword);
            }
            $keyword = strtok(' ');
        }
        $keywords = implode(' ', $searched_keywords);
        if (count($this->document_types) > 0) {
            $this->db->loadModule('Function');
            $params = array($this->db->quote($keywords, 'text'), $this->db->quote($keywords_hash, 'text'), $this->quoteArray($this->document_types), $this->db->quote($id, 'text'));
            $types = array('text');
            $rs = $this->db->function->executeStoredProc('nateGoSearch', $params, $types);
            if (MDB2::isError($rs)) {
                throw new NateGoSearchDBException($rs);
            }
            $unique_id = $rs->fetchOne();
            if (MDB2::isError($unique_id)) {
                throw new NateGoSearchDBException($unique_id);
            }
            $unique_counter++;
            $results->setUniqueId($unique_id);
            $sql = sprintf('select count(document_id) from %s
				where unique_id = %s', $results->getResultTable(), $this->db->quote($unique_id, 'text'));
            $document_count = $this->db->queryOne($sql);
            if (MDB2::isError($document_count)) {
                throw new NateGoSearchDBException($document_count);
            }
            $results->setDocumentCount($document_count);
        }
        return $results;
    }