<?PHP

#
#   FILE:  SPT--GlobalSearchEngine.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2005-2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/cwis/
#

/*
OUTSTANDING ISSUES:
- search string(s) must be escaped (~XX)
- search scores must be normalized
*/


class GlobalSearchEngine {

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Constructs a GlobalSearchEngine object.
    */
    function GlobalSearchEngine()
    {
    }

    /**
    * Performs a keyword search using a specified search string and returns the
    * results, starting with the one numbered with the starting result
    * (default 0) and continuing until reaching the desired number of results
    * (default 10).
    * @param string $SearchString The string to search for.
    * @param int $StartingResult The number of the starting result.  (OPTIONAL)
    * @param int $NumberOfResults The number of results to return.  (OPTIONAL)
    * @return The results of the specified search.
    */
    function Search($SearchString, $StartingResult = 0, $NumberOfResults = 10)
    {
        # save start time to use in calculating search time
        $StartTime = $this->GetMicrotime();

        # create OAI-SQ set specification from search string
        $SetSpec = "OAI-SQ!".$SearchString;

        # perform global search
        $SearchResults = $this->PerformSearch(
                $SetSpec, $StartingResult, $NumberOfResults);

        # record search time
        $this->LastSearchTime = $this->GetMicrotime() - $StartTime;

        # return results to caller
        return $SearchResults;
    }

    /**
    * Performs a search across multiple fields and returns the trimmed results
    * to the caller.
    * @param array $SearchStrings The strings to be searche for.
    * @param int $StartingResult The number of the starting result.  (OPTIONAL)
    * @param int $NumberOfResults The number of results to return.  (OPTIONAL)
    * @return The results of the fielded search.
    */
    function FieldedSearch($SearchStrings, $StartingResult = 0, $NumberOfResults = 10)
    {
    }

    /**
    * Gets the number of results returned in the last search.
    * @return The number of results returned in the the last search.
    */
    function NumberOfResults() {  return $this->NumberOfResultsAvailable;  }

    /**
    * Gets the time taken to perform the previous search, in microseconds.
    * @return The time taken to perform the previous search.
    */
    function SearchTime()
    {
        return $this->LastSearchTime;
    }


    # ---- PRIVATE INTERFACE -------------------------------------------------

    var $NumberOfResultsAvailable;
    var $LastSearchTime;

    /**
    * Performs an OAI-SQ search.
    * @param string $SetSpec The specification of a set of OAI resources
    * @param int $StartingResult The number of the starting result.
    * @param int $NumberOfResults The number of results to retun.
    * @return The results of the specified search.
    */
    function PerformSearch($SetSpec, $StartingResult, $NumberOfResults)
    {
        # for each global search site
        $DB = new Database();
        $DB->Query("SELECT * FROM GlobalSearchSites");
        $SearchResults = array();
        while ($SiteInfo = $DB->FetchRow())
        {
            # retrieve results from site
            $SiteSearchResults = $this->SearchSite($SiteInfo, $SetSpec);

            # add results to result list
            $SearchResults = array_merge($SearchResults, $SiteSearchResults);
        }

        /**
        * Compares the search score of two search results.
        * @param array $ResultA The first search result to be compared.
        * @param array $ResultB The second search result to be compared.
        * @return 1 if $ResultB has a larger search score, -1 if $ResultA has
        *       a larger search score, or 0 if the search scores are equal.
        */
        function SearchScoreCmp($ResultA, $ResultB)
        {
            return ($ResultA["Search Score"] == $ResultB["Search Score"]) ? 0
                    : (($ResultA["Search Score"] < $ResultB["Search Score"]) ? 1 : -1);
        }
        usort($SearchResults, "SearchScoreCmp");

        # save number of results found
        $this->NumberOfResultsAvailable = count($SearchResults);

        # trim result list to match range requested by caller
        $SearchResults = array_slice($SearchResults, $StartingResult, $NumberOfResults);

        # return search results to caller
        return $SearchResults;
    }

    /**
    * Searches one site with a specification of subset of records to be retrieved.
    * @param array $SiteInfo An array containing an OAI repository URL
    * @param string $SetSpec The specification of a set of OAI resources
    * @return The results of the search.
    */
    function SearchSite($SiteInfo, $SetSpec)
    {
        # create OAI client and perform query
        $Client = new OAIClient($SiteInfo["OaiUrl"]);
        $Client->SetSpec($SetSpec);
        $QueryResults = $Client->GetRecords();

        # for each result returned from query
        foreach ($QueryResults as $Result)
        {
            # extract and save result data where available
            unset($ResultData);
            $ResultData["Title"] =
                    isset($Result["metadata"]["DC:TITLE"][0])
                    ? $Result["metadata"]["DC:TITLE"][0] : NULL;
            $ResultData["Description"] =
                    isset($Result["metadata"]["DC:DESCRIPTION"][0])
                    ? $Result["metadata"]["DC:DESCRIPTION"][0] : NULL;
            $ResultData["Url"] =
                    isset($Result["metadata"]["DC:IDENTIFIER"][0])
                    ? $Result["metadata"]["DC:IDENTIFIER"][0] : NULL;
            $ResultData["Full Record Link"] =
                    isset($Result["about"]["SEARCHINFO"]["FULLRECORDLINK"][0])
                    ? $Result["about"]["SEARCHINFO"]["FULLRECORDLINK"][0] : NULL;
            $ResultData["Search Score"] =
                    isset($Result["about"]["SEARCHINFO"]["SEARCHSCORE"][0])
                    ? $Result["about"]["SEARCHINFO"]["SEARCHSCORE"][0] : NULL;
            $ResultData["Cumulative Rating"] =
                    isset($Result["about"]["SEARCHINFO"]["CUMULATIVERATING"][0])
                    ? $Result["about"]["SEARCHINFO"]["CUMULATIVERATING"][0] : NULL;
            $ResultData["Cumulative Rating Scale"] =
                    isset($Result["about"]["SEARCHINFO"]["CUMULATIVERATINGSCALE"][0])
                    ? $Result["about"]["SEARCHINFO"]["CUMULATIVERATINGSCALE"][0] : NULL;

            # save site info for result
            $ResultData["Site ID"] = $SiteInfo["SiteId"];
            $ResultData["Site Name"] = $SiteInfo["SiteName"];
            $ResultData["Site URL"] = $SiteInfo["SiteUrl"];

            # add result data to search results
            $SearchResults[] = $ResultData;
        }

        # return search results to caller
        return $SearchResults;
    }

    /**
    * Convenience function for getting the time in microseconds.
    * @return The time in microseconds.
    */
    function GetMicrotime()
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
}


?>
