/// <summary> Retrieves the table of search results from the cache  </summary>
        /// <param name="Current_Mode"> Mode / navigation information for the current request</param>
        /// <param name="Terms"> List of all search terms for the search result statistics to retrieve </param>
        /// <param name="Fields"> List of all search fields for the search result statistics to retrieve </param>
        /// <param name="DateRange_Start"> Beginning of a date range search, or -1 </param>
        /// <param name="DateRange_End"> End of a date range search, or -1 </param>
        /// <param name="Count"> Number of fields or terms to include in the key for this result </param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering</param>
        /// <returns> Either NULL or the search results item/title list </returns>
        public static Search_Results_Statistics Retrieve_Search_Result_Statistics(Results_Arguments Current_Mode, int Count, List<string> Fields, List<string> Terms, long DateRange_Start, long DateRange_End, Custom_Tracer Tracer)
        {
            // If the cache is disabled, just return before even tracing
            if (Settings.Disabled)
                return null;

            if (Tracer != null)
            {
                Tracer.Add_Trace("CachedDataManager.Retrieve_Search_Result_Statistics", "");
            }

            // Determine the key
            // If there is no aggregation listed, use 'all'
            string aggregation_code = Current_Mode.Aggregation.ToLower();
            if (aggregation_code.Length == 0)
                aggregation_code = "all";

            // Determine the search precision
            string precision = "results";
            switch (Current_Mode.Search_Precision)
            {
                case Search_Precision_Type_Enum.Contains:
                    precision = "contains";
                    break;

                case Search_Precision_Type_Enum.Exact_Match:
                    precision = "exact";
                    break;

                case Search_Precision_Type_Enum.Synonmic_Form:
                    precision = "like";
                    break;
            }

            // Start to build the key
            StringBuilder keyBuilder = new StringBuilder("TOTALRESULTS_" + precision + "_" + aggregation_code + "_T_");
            for (int i = 0; i < Count; i++)
            {
                keyBuilder.Append(Terms[i].ToLower() + "_");
            }
            keyBuilder.Append("F_");
            for (int i = 0; i < Count; i++)
            {
                keyBuilder.Append(Fields[i] + "_");
            }

            // Add possivle date range search restrction to the key
            if (DateRange_Start >= 0)
            {
                keyBuilder.Append("_DATE" + DateRange_Start);
                if (DateRange_End >= 0)
                {
                    keyBuilder.Append("-" + DateRange_End);
                }
            }

            string key = keyBuilder.ToString();

            //if (Current_Mode.SubAggregation.Length > 0)
            //{
            //    key = "a_" + precision + "_" + aggregation_code + "s_" + Current_Mode.SubAggregation + "t_" + Current_Mode.Search_String + "f_" + search_fields;
            //}
            if ((String.IsNullOrEmpty(Current_Mode.Search_String)) && (!String.IsNullOrEmpty(Current_Mode.Coordinates)))
            {
                key = "TOTALRESULTS_" + precision + "_" + aggregation_code + "coord_" + Current_Mode.Coordinates;
            }

            // Try to get this from the local cache first
            object returnValue = HttpContext.Current.Cache.Get(key);
            if (returnValue != null)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("CachedDataManager.Retrieve_Search_Result_Statistics", "Results pulled from local cache");
                }

                return (Search_Results_Statistics)returnValue;
            }

            return null;
        }
        /// <summary> Display search results in simple XML format </summary>
        /// <param name="Output"> Stream to which to write the text for this main writer </param>
        /// <param name="Args"></param>
        /// <param name="ResultsStats"></param>
        /// <param name="ResultsPage"></param>
        protected internal void legacy_xml_display_search_results(TextWriter Output, Results_Arguments Args, Search_Results_Statistics ResultsStats, List<iSearch_Title_Result> ResultsPage)
        {
            // Get the URL and network roots
            string image_url = Engine_ApplicationCache_Gateway.Settings.Servers.Image_URL;
            string network = Engine_ApplicationCache_Gateway.Settings.Servers.Image_Server_Network;
            string base_url = Engine_ApplicationCache_Gateway.Settings.Servers.Base_URL;
            if (HttpContext.Current != null)
            {
                base_url = HttpContext.Current.Request.Url.AbsoluteUri;
                if (base_url.IndexOf("?") > 0)
                    base_url = base_url.Substring(0, base_url.IndexOf("?")).Replace("sobekcm.svc", "");
            }
            if ((base_url.Length > 0) && (base_url[base_url.Length - 1] != '/'))
                base_url = base_url + "/";
            if ((image_url.Length > 0) && (image_url[image_url.Length - 1] != '/'))
                image_url = image_url + "/";

            // Write the header first
            Output.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\" ?> ");
            Output.WriteLine("<ResultSet Page=\"" + Args.Page + "\" Total=\"" + ResultsStats.Total_Titles + "\">");

            // Now, add XML for each title
            string lastBibID = string.Empty;
            foreach (iSearch_Title_Result thisResult in ResultsPage)
            {
                if (thisResult.BibID != lastBibID)
                {
                    if (lastBibID.Length > 0)
                        Output.WriteLine("</TitleResult>");
                    Output.WriteLine("<TitleResult ID=\"" + thisResult.BibID + "\">");
                    lastBibID = thisResult.BibID;
                }

                // Determine folder from BibID
                string folder = thisResult.BibID.Substring(0, 2) + "/" + thisResult.BibID.Substring(2, 2) + "/" + thisResult.BibID.Substring(4, 2) + "/" + thisResult.BibID.Substring(6, 2) + "/" + thisResult.BibID.Substring(8);

                // Now, add XML for each item
                for (int i = 0; i < thisResult.Item_Count; i++)
                {
                    iSearch_Item_Result itemResult = thisResult.Get_Item(i);
                    Output.WriteLine("\t<ItemResult ID=\"" + thisResult.BibID + "_" + itemResult.VID + "\">");
                    Output.Write("\t\t<Title>");
                    Write_XML(Output, itemResult.Title);
                    Output.WriteLine("</Title>");
                    if ( !String.IsNullOrEmpty(itemResult.PubDate))
                    {
                        Output.Write("\t\t<Date>");
                        Write_XML(Output, itemResult.PubDate);
                        Output.WriteLine("</Date>");
                    }
                    Output.WriteLine("\t\t<Location>");
                    Output.WriteLine("\t\t\t<URL>" + base_url + thisResult.BibID + "/" + itemResult.VID + "</URL>");

                    if (!String.IsNullOrEmpty(itemResult.MainThumbnail))
                    {
                        Output.WriteLine("\t\t\t<MainThumb>" + image_url + folder + "/" + itemResult.VID + "/" + itemResult.MainThumbnail + "</MainThumb>");
                    }

                    Output.WriteLine("\t\t\t<Folder type=\"web\">" + image_url + folder + "/" + itemResult.VID + "</Folder>");
                    Output.WriteLine("\t\t\t<Folder type=\"network\">" + network + folder.Replace("/", "\\") + "\\" + itemResult.VID + "</Folder>");
                    Output.WriteLine("\t\t</Location>");
                    Output.WriteLine("\t</ItemResult>");
                }
            }

            if (ResultsPage.Count > 0)
                Output.WriteLine("</TitleResult>");
            Output.WriteLine("</ResultSet>");
        }
        /// <summary> Stores the table of search results to the cache  </summary>
        /// <param name="Current_Mode"> Mode / navigation information for the current request</param>
        /// <param name="Terms"> List of all search terms for the search result statistics to store </param>
        /// <param name="Fields"> List of all search fields for the search result statistics to store </param>
        /// <param name="Count"> Number of fields or terms to include in the key for this result </param>
        /// <param name="DateRange_Start"> Beginning of a date range search, or -1 </param>
        /// <param name="DateRange_End"> End of a date range search, or -1 </param>
        /// <param name="StoreObject"> Search results item/title list </param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering</param>
        public static void Store_Search_Result_Statistics(Results_Arguments Current_Mode, int Count, List<string> Fields, List<string> Terms, long DateRange_Start, long DateRange_End, Search_Results_Statistics StoreObject, Custom_Tracer Tracer)
        {
            // If the cache is disabled, just return before even tracing
            if (Settings.Disabled)
                return;
            // Determine the key
            // If there is no aggregation listed, use 'all'
            string aggregation_code = Current_Mode.Aggregation.ToLower();
            if (aggregation_code.Length == 0)
                aggregation_code = "all";

            // Determine the search precision
            string precision = "results";
            switch (Current_Mode.Search_Precision)
            {
                case Search_Precision_Type_Enum.Contains:
                    precision = "contains";
                    break;

                case Search_Precision_Type_Enum.Exact_Match:
                    precision = "exact";
                    break;

                case Search_Precision_Type_Enum.Synonmic_Form:
                    precision = "like";
                    break;
            }

            // Start to build the key
            StringBuilder keyBuilder = new StringBuilder("TOTALRESULTS_" + precision + "_" + aggregation_code + "_T_");
            for (int i = 0; i < Count; i++)
            {
                keyBuilder.Append(Terms[i].ToLower() + "_");
            }
            keyBuilder.Append("F_");
            for (int i = 0; i < Count; i++)
            {
                keyBuilder.Append(Fields[i] + "_");
            }

            // Add possivle date range search restrction to the key
            if (DateRange_Start >= 0)
            {
                keyBuilder.Append("_DATE" + DateRange_Start);
                if (DateRange_End >= 0)
                {
                    keyBuilder.Append("-" + DateRange_End);
                }
            }

            string key = keyBuilder.ToString();

            //if (Current_Mode.SubAggregation.Length > 0)
            //{
            //    key = "a_" + precision + "_" + aggregation_code + "s_" + Current_Mode.SubAggregation + "t_" + Current_Mode.Search_String + "f_" + search_fields;
            //}
            if ((String.IsNullOrEmpty(Current_Mode.Search_String)) && (!String.IsNullOrEmpty(Current_Mode.Coordinates)))
            {
                key = "TOTALRESULTS_" + precision + "_" + aggregation_code + "coord_" + Current_Mode.Coordinates;
            }

            // Store this on the local cache, if not there and storing on the cache server failed
            if (HttpContext.Current.Cache[key] == null)
            {
                if (Tracer != null)
                {
                    Tracer.Add_Trace("CachedDataManager.Store_Search_Result_Statistics", "Adding object '" + key + "' to the local cache with expiration of 3 minutes");
                }

                HttpContext.Current.Cache.Insert(key, StoreObject, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(3));
            }
        }
        /// <summary> Writes the search or browse information in JSON format directly to the output stream  </summary>
        /// <param name="Output"> Stream to which to write the JSON search or browse information </param>
        /// <param name="Args"></param>
        /// <param name="ResultsStats"></param>
        /// <param name="ResultsPage"></param>
        protected internal void legacy_json_display_search_results(TextWriter Output, Results_Arguments Args, Search_Results_Statistics ResultsStats, List<iSearch_Title_Result> ResultsPage)
        {
            // If results are null, or no results, return empty string
            if ((ResultsPage == null) || (ResultsStats == null) || (ResultsStats.Total_Items <= 0))
                return;

            // Get the URL and network roots
            string image_url = Engine_ApplicationCache_Gateway.Settings.Servers.Image_URL;
            string base_url = Engine_ApplicationCache_Gateway.Settings.Servers.Base_URL;
            if (HttpContext.Current != null)
            {
                base_url = HttpContext.Current.Request.Url.AbsoluteUri;
                if (base_url.IndexOf("?") > 0)
                    base_url = base_url.Substring(0, base_url.IndexOf("?")).Replace("sobekcm.svc", "");
            }
            if ((base_url.Length > 0) && (base_url[base_url.Length - 1] != '/'))
                base_url = base_url + "/";
            if ((image_url.Length > 0) && (image_url[image_url.Length - 1] != '/'))
                image_url = image_url + "/";

            Output.Write("[");

            // Step through all the results
            int i = 1;
            foreach (iSearch_Title_Result titleResult in ResultsPage)
            {
                // Always get the first item for things like the main link and thumbnail
                iSearch_Item_Result firstItemResult = titleResult.Get_Item(0);

                // Determine a thumbnail
                string thumb = image_url + titleResult.BibID.Substring(0, 2) + "/" + titleResult.BibID.Substring(2, 2) + "/" + titleResult.BibID.Substring(4, 2) + "/" + titleResult.BibID.Substring(6, 2) + "/" + titleResult.BibID.Substring(8) + "/" + firstItemResult.VID + "/" + firstItemResult.MainThumbnail;
                if ((thumb.ToUpper().IndexOf(".JPG") < 0) && (thumb.ToUpper().IndexOf(".GIF") < 0))
                {
                    thumb = String.Empty;
                }
                thumb = thumb.Replace("\\", "/").Replace("//", "/").Replace("http:/", "http://");

                // Was a previous item/title included here?
                if (i > 1)
                    Output.Write(",");
                Output.Write("{\"collection_item\":{\"name\":\"" + firstItemResult.Title.Trim().Replace("\"", "'") + "\",\"url\":\"" + base_url + titleResult.BibID + "/" + firstItemResult.VID + "\",\"collection_code\":\"\",\"id\":\"" + titleResult.BibID + "_" + firstItemResult.VID + "\",\"thumb_url\":\"" + thumb + "\"}}");

                i++;
            }

            Output.Write("]");
        }
        /// <summary> Get just the search statistics information for a search or browse </summary>
        /// <param name="Response"></param>
        /// <param name="UrlSegments"></param>
        /// <param name="QueryString"></param>
        /// <param name="Protocol"></param>
        /// <param name="IsDebug"></param>
        public void Get_Search_Statistics(HttpResponse Response, List<string> UrlSegments, NameValueCollection QueryString, Microservice_Endpoint_Protocol_Enum Protocol, bool IsDebug )
        {
            Custom_Tracer tracer = new Custom_Tracer();
            tracer.Add_Trace("ResultsServices.Get_Search_Statistics", "Parse request to determine search requested");

            // Get all the searh field necessary from the query string
            Results_Arguments args = new Results_Arguments(QueryString);

            // Was a collection indicated?
            if (UrlSegments.Count > 0)
                args.Aggregation = UrlSegments[0];

            // Get the aggregation object (we need to know which facets to use, etc.. )
            tracer.Add_Trace("ResultsServices.Get_Search_Statistics", "Get the '" + args.Aggregation + "' item aggregation (for facets, etc..)");
            Complete_Item_Aggregation aggr = AggregationServices.get_complete_aggregation(args.Aggregation, true, tracer);

            // If no aggregation was returned, that is an error
            if (aggr == null)
            {
                tracer.Add_Trace("ResultsServices.Get_Search_Statistics", "Returned aggregation was NULL... aggregation code may not be valid");

                if ( IsDebug )
                {
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("DEBUG MODE DETECTED");
                    Response.Output.WriteLine();
                    Response.Output.WriteLine(tracer.Text_Trace);
                    return;
                }

                Response.ContentType = "text/plain";
                Response.Output.WriteLine("Error occurred or aggregation '" + args.Aggregation + "' not valid");
                Response.StatusCode = 500;
                return;
            }

            // Perform the search
            tracer.Add_Trace("ResultsServices.Get_Search_Statistics", "Perform the search");
            Search_Results_Statistics resultsStats;
            List<iSearch_Title_Result> resultsPage;
            ResultsEndpointErrorEnum error = Get_Search_Results(args, aggr, tracer, out resultsStats, out resultsPage);

            // Was this in debug mode?
            // If this was debug mode, then just write the tracer
            if ( IsDebug )
            {
                Response.ContentType = "text/plain";
                Response.Output.WriteLine("DEBUG MODE DETECTED");
                Response.Output.WriteLine();
                Response.Output.WriteLine(tracer.Text_Trace);
                return;
            }

            // If an error occurred, return the error
            switch (error)
            {
                case ResultsEndpointErrorEnum.Database_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Database exception");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Database_Timeout_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Database timeout");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Solr_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Solr exception");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Unknown:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Unknown error");
                    Response.StatusCode = 500;
                    return;
            }

            // Get the JSON-P callback function
            string json_callback = "parseResultsStats";
            if ((Protocol == Microservice_Endpoint_Protocol_Enum.JSON_P) && (!String.IsNullOrEmpty(QueryString["callback"])))
            {
                json_callback = QueryString["callback"];
            }

            // Use the base class to serialize the object according to request protocol
            Serialize(resultsStats, Response, Protocol, json_callback);
        }
        /// <summary> Get just the search statistics information for a search or browse </summary>
        /// <param name="Response"></param>
        /// <param name="UrlSegments"></param>
        /// <param name="QueryString"></param>
        /// <param name="Protocol"></param>
        /// <param name="IsDebug"></param>
        public void Get_Search_Results_Page(HttpResponse Response, List<string> UrlSegments, NameValueCollection QueryString, Microservice_Endpoint_Protocol_Enum Protocol, bool IsDebug )
        {
            Custom_Tracer tracer = new Custom_Tracer();
            tracer.Add_Trace("ResultsServices.Get_Search_Results_Set", "Parse request to determine search requested");

            // Get all the searh field necessary from the query string
            Results_Arguments args = new Results_Arguments(QueryString);

            // Was a collection indicated?
            if (UrlSegments.Count > 0)
                args.Aggregation = UrlSegments[0];

            // Get the aggregation object (we need to know which facets to use, etc.. )
            tracer.Add_Trace("ResultsServices.Get_Search_Results_Set", "Get the '" + args.Aggregation + "' item aggregation (for facets, etc..)");
            Complete_Item_Aggregation aggr = AggregationServices.get_complete_aggregation(args.Aggregation, true, tracer);

            // If no aggregation was returned, that is an error
            if (aggr == null)
            {
                tracer.Add_Trace("ResultsServices.Get_Search_Results_Set", "Returned aggregation was NULL... aggregation code may not be valid");

                if ( IsDebug )
                {
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("DEBUG MODE DETECTED");
                    Response.Output.WriteLine();
                    Response.Output.WriteLine(tracer.Text_Trace);
                    return;
                }

                Response.ContentType = "text/plain";
                Response.Output.WriteLine("Error occurred or aggregation '" + args.Aggregation + "' not valid");
                Response.StatusCode = 500;
                return;
            }

            // Perform the search
            tracer.Add_Trace("ResultsServices.Get_Search_Results_Set", "Perform the search");
            Search_Results_Statistics resultsStats;
            List<iSearch_Title_Result> resultsPage;
            ResultsEndpointErrorEnum error = Get_Search_Results(args, aggr, tracer, out resultsStats, out resultsPage);

            // Map to the results object title / item
            tracer.Add_Trace("ResultsServices.Get_Search_Results_Set", "Map to the results object title / item");
            List<ResultTitleInfo> results = new List<ResultTitleInfo>();
            foreach (iSearch_Title_Result thisResult in resultsPage)
            {
                // Create the new rest title object
                ResultTitleInfo restTitle = new ResultTitleInfo
                {
                    BibID = thisResult.BibID,
                    MainThumbnail = thisResult.GroupThumbnail,
                    Title = thisResult.GroupTitle
                };

                // add each descriptive field over
                int field_index = 0;
                foreach (string metadataTerm in resultsStats.Metadata_Labels)
                {
                    if ( !String.IsNullOrWhiteSpace(thisResult.Metadata_Display_Values[field_index]))
                    {
                        string termString = thisResult.Metadata_Display_Values[field_index];
                        ResultTitle_DescriptiveTerm termObj = new ResultTitle_DescriptiveTerm(metadataTerm);
                        if (termString.IndexOf("|") > 0)
                        {
                            string[] splitter = termString.Split("|".ToCharArray());
                            foreach (string thisSplit in splitter)
                            {
                                if ( !String.IsNullOrWhiteSpace(thisSplit))
                                    termObj.Add_Value(thisSplit.Trim());
                            }
                        }
                        else
                        {
                            termObj.Add_Value(termString.Trim());
                        }
                        restTitle.Description.Add(termObj);
                    }
                    field_index++;
                }

                // Add each item
                for (int i = 0; i < thisResult.Item_Count; i++)
                {
                    iSearch_Item_Result itemResults = thisResult.Get_Item(i);

                    ResultItemInfo newItem = new ResultItemInfo
                    {
                        VID = itemResults.VID,
                        Title = itemResults.Title,
                        Link = itemResults.Link,
                        MainThumbnail = itemResults.MainThumbnail
                    };

                    restTitle.Items.Add(newItem);
                }

                // Add to the array
                results.Add(restTitle);

            }

            // Was this in debug mode?
            // If this was debug mode, then just write the tracer
            if ( IsDebug )
            {
                Response.ContentType = "text/plain";
                Response.Output.WriteLine("DEBUG MODE DETECTED");
                Response.Output.WriteLine();
                Response.Output.WriteLine(tracer.Text_Trace);
                return;
            }

            // If an error occurred, return the error
            switch (error)
            {
                case ResultsEndpointErrorEnum.Database_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Database exception");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Database_Timeout_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Database timeout");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Solr_Exception:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Solr exception");
                    Response.StatusCode = 500;
                    return;

                case ResultsEndpointErrorEnum.Unknown:
                    Response.ContentType = "text/plain";
                    Response.Output.WriteLine("Unknown error");
                    Response.StatusCode = 500;
                    return;
            }

            // Get the JSON-P callback function
            string json_callback = "parseResultsSet";
            if ((Protocol == Microservice_Endpoint_Protocol_Enum.JSON_P) && (!String.IsNullOrEmpty(QueryString["callback"])))
            {
                json_callback = QueryString["callback"];
            }

            // Create the return object
            ResultSetPage wrappedObject = new ResultSetPage();
            wrappedObject.Results = results;
            wrappedObject.Page = args.Page;

            // Use the base class to serialize the object according to request protocol
            Serialize(wrappedObject, Response, Protocol, json_callback);
        }
        /// <summary> Performs a search ( or retrieves the search results from the cache ) and outputs the results and search url used  </summary>
        /// <param name="Current_Mode"> Mode / navigation information for the current request</param>
        /// <param name="Aggregation_Object"> Object for the current aggregation object, against which this search is performed </param>
        /// <param name="Tracer"> Trace object keeps a list of each method executed and important milestones in rendering </param>
        /// <param name="Complete_Result_Set_Info"> [OUT] Information about the entire set of results </param>
        /// <param name="Paged_Results"> [OUT] List of search results for the requested page of results </param>
        public ResultsEndpointErrorEnum Get_Search_Results(Results_Arguments Current_Mode,
                                       Complete_Item_Aggregation Aggregation_Object, 
                                       Custom_Tracer Tracer,
                                       out Search_Results_Statistics Complete_Result_Set_Info,
                                       out List<iSearch_Title_Result> Paged_Results)
        {
            Tracer.Add_Trace("SobekCM_Assistant.Get_Search_Results", String.Empty);

            // Set output initially to null
            Paged_Results = null;
            Complete_Result_Set_Info = null;

            // Get the sort
            int sort = Current_Mode.Sort.HasValue ? Math.Max(Current_Mode.Sort.Value, ((ushort)1)) : 0;
            if ((sort != 0) && (sort != 1) && (sort != 2) && (sort != 10) && (sort != 11))
                sort = 0;

            // If there was no search, it is a browse
            if (String.IsNullOrEmpty(Current_Mode.Search_String))
            {
                // Get the page count in the results
                int current_page_index = Current_Mode.Page;

                // Set the flags for how much data is needed.  (i.e., do we need to pull ANYTHING?  or
                // perhaps just the next page of results ( as opposed to pulling facets again).
                bool need_browse_statistics = true;
                bool need_paged_results = true;
                if (Current_Mode.Use_Cache)
                {
                    // Look to see if the browse statistics are available on any cache for this browse
                    Complete_Result_Set_Info = CachedDataManager.Retrieve_Browse_Result_Statistics(Aggregation_Object.Code, "all", Tracer);
                    if (Complete_Result_Set_Info != null)
                        need_browse_statistics = false;

                    // Look to see if the paged results are available on any cache..
                    Paged_Results = CachedDataManager.Retrieve_Browse_Results(Aggregation_Object.Code, "all", current_page_index, sort, Tracer);
                    if (Paged_Results != null)
                        need_paged_results = false;
                }

                // Was a copy found in the cache?
                if ((!need_browse_statistics) && (!need_paged_results))
                {
                    if (Tracer != null)
                    {
                        Tracer.Add_Trace("SobekCM_Assistant.Get_Browse_Info", "Browse statistics and paged results retrieved from cache");
                    }
                }
                else
                {
                    if (Tracer != null)
                    {
                        Tracer.Add_Trace("SobekCM_Assistant.Get_Browse_Info", "Building results information");
                    }

                    // Try to pull more than one page, so we can cache the next page or so
                    List<List<iSearch_Title_Result>> pagesOfResults;

                    // Get from the hierarchy object
                        Multiple_Paged_Results_Args returnArgs = Item_Aggregation_Utilities.Gat_All_Browse(Aggregation_Object, current_page_index, sort, (int) Current_Mode.ResultsPerPage, Current_Mode.Use_Cache, need_browse_statistics, Tracer);
                        if (need_browse_statistics)
                        {
                            Complete_Result_Set_Info = returnArgs.Statistics;
                            foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                            {
                                Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                thisFacet.MetadataTerm = field.Facet_Term;
                            }
                        }
                        pagesOfResults = returnArgs.Paged_Results;
                        if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                            Paged_Results = pagesOfResults[0];

                    // Save the overall result set statistics to the cache if something was pulled
                    if (Current_Mode.Use_Cache)
                    {
                        if ((need_browse_statistics) && (Complete_Result_Set_Info != null))
                        {
                            CachedDataManager.Store_Browse_Result_Statistics(Aggregation_Object.Code, "all", Complete_Result_Set_Info, Tracer);
                        }

                        // Save the overall result set statistics to the cache if something was pulled
                        if ((need_paged_results) && (Paged_Results != null))
                        {
                            CachedDataManager.Store_Browse_Results(Aggregation_Object.Code, "all", current_page_index, sort, pagesOfResults, Tracer);
                        }
                    }
                }

                return ResultsEndpointErrorEnum.NONE;
            }

            // Depending on type of search, either go to database or Greenstone
            if (Current_Mode.Search_Type == Search_Type_Enum.Map)
            {
                try
                {
                    double lat1 = 1000;
                    double long1 = 1000;
                    double lat2 = 1000;
                    double long2 = 1000;
                    string[] terms = Current_Mode.Coordinates.Split(",".ToCharArray());
                    if (terms.Length < 4)
                    {
                        lat1 = Convert.ToDouble(terms[0]);
                        lat2 = lat1;
                        long1 = Convert.ToDouble(terms[1]);
                        long2 = long1;
                    }
                    if (terms.Length >= 4)
                    {
                        if (terms[0].Length > 0)
                            lat1 = Convert.ToDouble(terms[0]);
                        if (terms[1].Length > 0)
                            long1 = Convert.ToDouble(terms[1]);
                        if (terms[2].Length > 0)
                            lat2 = Convert.ToDouble(terms[2]);
                        if (terms[3].Length > 0)
                            long2 = Convert.ToDouble(terms[3]);
                    }

                    // If just the first point is valid, use that
                    if ((lat2 == 1000) || (long2 == 1000))
                    {
                        lat2 = lat1;
                        long2 = long1;
                    }

                    // If just the second point is valid, use that
                    if ((lat1 == 1000) || (long1 == 1000))
                    {
                        lat1 = lat2;
                        long1 = long2;
                    }

                    // Perform the search against the database
                    try
                    {
                        // Get the page count in the results
                        int current_page_index = Current_Mode.Page;

                        // Try to pull more than one page, so we can cache the next page or so
                        Multiple_Paged_Results_Args returnArgs = Engine_Database.Get_Items_By_Coordinates(Current_Mode.Aggregation, lat1, long1, lat2, long2, false, 20, current_page_index, sort, false, new List<short>(), true, Tracer);
                        List<List<iSearch_Title_Result>> pagesOfResults = returnArgs.Paged_Results;
                        Complete_Result_Set_Info = returnArgs.Statistics;

                        if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                            Paged_Results = pagesOfResults[0];
                    }
                    catch (Exception ee)
                    {
                        // Next, show the message to the user
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing database coordinate search");
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                        Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);

                        return ee.Message.ToUpper().IndexOf("TIMEOUT") >= 0 ? ResultsEndpointErrorEnum.Database_Timeout_Exception : ResultsEndpointErrorEnum.Database_Exception;
                    }
                }
                catch (Exception ee )
                {
                    // Next, show the message to the user
                    Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                    Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);
                    return ResultsEndpointErrorEnum.Unknown;
                }
            }
            else
            {
                List<string> terms = new List<string>();
                List<string> web_fields = new List<string>();

                // Split the terms correctly ( only use the database stop words for the split if this will go to the database ultimately)
                if ((Current_Mode.Search_Type == Search_Type_Enum.Full_Text) || (Current_Mode.Search_Fields.IndexOf("TX") >= 0))
                {
                    Split_Clean_Search_Terms_Fields(Current_Mode.Search_String, Current_Mode.Search_Fields, Current_Mode.Search_Type, terms, web_fields, null, Current_Mode.Search_Precision, ',');
                }
                else
                {
                    Split_Clean_Search_Terms_Fields(Current_Mode.Search_String, Current_Mode.Search_Fields, Current_Mode.Search_Type, terms, web_fields, Engine_ApplicationCache_Gateway.StopWords, Current_Mode.Search_Precision, ',');
                }

                // Get the count that will be used
                int actualCount = Math.Min(terms.Count, web_fields.Count);

                // Determine if this is a special search type which returns more rows and is not cached.
                // This is used to return the results as XML and DATASET
                int results_per_page = (int) Current_Mode.ResultsPerPage;

                // Determine if a date range was provided
                long date1 = -1;
                long date2 = -1;
                if (Current_Mode.DateRange_Date1.HasValue)
                {
                    date1 = Current_Mode.DateRange_Date1.Value;
                    if (Current_Mode.DateRange_Date2.HasValue)
                    {
                        if (Current_Mode.DateRange_Date2.Value >= Current_Mode.DateRange_Date1.Value)
                            date2 = Current_Mode.DateRange_Date2.Value;
                        else
                        {
                            date1 = Current_Mode.DateRange_Date2.Value;
                            date2 = Current_Mode.DateRange_Date1.Value;
                        }
                    }
                    else
                    {
                        date2 = date1;
                    }
                }
                if (date1 < 0)
                {
                    if ((Current_Mode.DateRange_Year1.HasValue) && (Current_Mode.DateRange_Year1.Value > 0))
                    {
                        DateTime startDate = new DateTime(Current_Mode.DateRange_Year1.Value, 1, 1);
                        TimeSpan timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                        date1 = (long)timeElapsed.TotalDays;
                        if ((Current_Mode.DateRange_Year2.HasValue) && (Current_Mode.DateRange_Year2.Value > 0))
                        {
                            startDate = new DateTime(Current_Mode.DateRange_Year2.Value, 12, 31);
                            timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                            date2 = (long)timeElapsed.TotalDays;
                        }
                        else
                        {
                            startDate = new DateTime(Current_Mode.DateRange_Year1.Value, 12, 31);
                            timeElapsed = startDate.Subtract(new DateTime(1, 1, 1));
                            date2 = (long)timeElapsed.TotalDays;
                        }
                    }
                }

                // Set the flags for how much data is needed.  (i.e., do we need to pull ANYTHING?  or
                // perhaps just the next page of results ( as opposed to pulling facets again).
                bool need_search_statistics = true;
                bool need_paged_results = true;
                if (Current_Mode.Use_Cache)
                {
                    // Look to see if the search statistics are available on any cache..
                    Complete_Result_Set_Info = CachedDataManager.Retrieve_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Tracer);
                    if (Complete_Result_Set_Info != null)
                        need_search_statistics = false;

                    // Look to see if the paged results are available on any cache..
                    Paged_Results = CachedDataManager.Retrieve_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, Tracer);
                    if (Paged_Results != null)
                        need_paged_results = false;
                }

                // If both were retrieved, do nothing else
                if ((need_paged_results) || (need_search_statistics))
                {
                    // Should this pull the search from the database, or from greenstone?
                    if ((Current_Mode.Search_Type == Search_Type_Enum.Full_Text) || (Current_Mode.Search_Fields.IndexOf("TX") >= 0))
                    {
                        try
                        {
                            // Get the page count in the results
                            int current_page_index = Current_Mode.Page;

                            // Perform the search against greenstone
                            Search_Results_Statistics recomputed_search_statistics;
                            Perform_Solr_Search(Tracer, terms, web_fields, actualCount, Current_Mode.Aggregation, current_page_index, sort, results_per_page, out recomputed_search_statistics, out Paged_Results);
                            if (need_search_statistics)
                            {
                                Complete_Result_Set_Info = recomputed_search_statistics;

                                foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                                {
                                    Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                    thisFacet.MetadataTerm = field.Facet_Term;
                                }
                            }
                        }
                        catch (Exception ee)
                        {
                            // Next, show the message to the user
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing solr/lucene search");
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);
                            return ResultsEndpointErrorEnum.Unknown;
                        }

                        // If this was a special search, don't cache this
                        if (!Current_Mode.Use_Cache)
                        {
                            // Cache the search statistics, if it was needed
                            if ((need_search_statistics) && (Complete_Result_Set_Info != null))
                            {
                                CachedDataManager.Store_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Complete_Result_Set_Info, Tracer);
                            }

                            // Cache the search results
                            if ((need_paged_results) && (Paged_Results != null))
                            {
                                CachedDataManager.Store_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, Paged_Results, Tracer);
                            }
                        }
                    }
                    else
                    {
                        // Try to pull more than one page, so we can cache the next page or so
                        List<List<iSearch_Title_Result>> pagesOfResults;

                        // Perform the search against the database
                        try
                        {
                            Search_Results_Statistics recomputed_search_statistics;
                            Perform_Database_Search(Tracer, terms, web_fields, date1, date2, actualCount, Current_Mode, sort, Aggregation_Object, results_per_page, Current_Mode.Use_Cache, out recomputed_search_statistics, out pagesOfResults, need_search_statistics);
                            if (need_search_statistics)
                            {
                                Complete_Result_Set_Info = recomputed_search_statistics;

                                foreach (Search_Facet_Collection thisFacet in Complete_Result_Set_Info.Facet_Collections)
                                {
                                    Metadata_Search_Field field = Engine_ApplicationCache_Gateway.Settings.Metadata_Search_Field_By_ID(thisFacet.MetadataTypeID);
                                    thisFacet.MetadataTerm = field.Facet_Term;
                                }
                            }

                            if ((pagesOfResults != null) && (pagesOfResults.Count > 0))
                                Paged_Results = pagesOfResults[0];
                        }
                        catch (Exception ee)
                        {
                            // Next, show the message to the user
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception caught while performing database coordinate search");
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", "Exception: " + ee.Message);
                            Tracer.Add_Trace("ResultServices.Get_Search_Results", ee.StackTrace);

                            return ee.Message.ToUpper().IndexOf("TIMEOUT") >= 0 ? ResultsEndpointErrorEnum.Database_Timeout_Exception : ResultsEndpointErrorEnum.Database_Exception;
                        }

                        // If this was a special search, don't cache this
                        if (!Current_Mode.Use_Cache)
                        {
                            // Cache the search statistics, if it was needed
                            if ((need_search_statistics) && (Complete_Result_Set_Info != null))
                            {
                                CachedDataManager.Store_Search_Result_Statistics(Current_Mode, actualCount, web_fields, terms, date1, date2, Complete_Result_Set_Info, Tracer);
                            }

                            // Cache the search results
                            if ((need_paged_results) && (pagesOfResults != null))
                            {
                                CachedDataManager.Store_Search_Results(Current_Mode, sort, actualCount, web_fields, terms, date1, date2, pagesOfResults, Tracer);
                            }
                        }
                    }
                }
            }

            return ResultsEndpointErrorEnum.NONE;
        }
        private void Perform_Database_Search(Custom_Tracer Tracer, List<string> Terms, List<string> Web_Fields, long Date1, long Date2, int ActualCount, Results_Arguments Current_Mode, int Current_Sort, Complete_Item_Aggregation Aggregation_Object, int Results_Per_Page, bool Potentially_Include_Facets, out Search_Results_Statistics Complete_Result_Set_Info, out List<List<iSearch_Title_Result>> Paged_Results, bool Need_Search_Statistics)
        {
            if (Tracer != null)
            {
                Tracer.Add_Trace("SobekCM_Assistant.Perform_Database_Search", "Query the database for search results");
            }

            // Get the list of facets first
            List<short> facetsList = Aggregation_Object.Facets;
            if (!Potentially_Include_Facets)
                facetsList.Clear();

            // Set the return values to NULL initially
            Complete_Result_Set_Info = null;

            const bool INCLUDE_PRIVATE = false;

            List<short> links = new List<short>();
            List<short> db_fields = new List<short>();
            List<string> db_terms = Terms.ToList();

            // Step through all the web fields and convert to db fields
            for (int i = 0; i < ActualCount; i++)
            {
                if (Web_Fields[i].Length > 1)
                {
                    // Find the joiner
                    if ((Web_Fields[i][0] == '+') || (Web_Fields[i][0] == '=') || (Web_Fields[i][0] == '-'))
                    {
                        if (Web_Fields[i][0] == '+')
                            links.Add(0);
                        if (Web_Fields[i][0] == '=')
                            links.Add(1);
                        if (Web_Fields[i][0] == '-')
                            links.Add(2);

                        Web_Fields[i] = Web_Fields[i].Substring(1);
                    }
                    else
                    {
                        links.Add(0);
                    }

                    // Find the db field number
                    db_fields.Add(Metadata_Field_Number(Web_Fields[i]));
                }

                // Also add starting and ending quotes to all the valid searches
                if (db_terms[i].Length > 0)
                {
                    if ((db_terms[i].IndexOf("\"") < 0) && (db_terms[i].IndexOf(" ") < 0))
                    {
                        // Since this is a single word, see what type of special codes to include
                        switch (Current_Mode.Search_Precision)
                        {
                            case Search_Precision_Type_Enum.Contains:
                                db_terms[i] = "\"" + db_terms[i] + "\"";
                                break;

                            case Search_Precision_Type_Enum.Inflectional_Form:
                                // If there are any non-characters, don't use inflectional for this term
                                bool inflectional = db_terms[i].All(Char.IsLetter);
                                if (inflectional)
                                {
                                    db_terms[i] = "FORMSOF(inflectional," + db_terms[i] + ")";
                                }
                                else
                                {
                                    db_terms[i] = "\"" + db_terms[i] + "\"";
                                }
                                break;

                            case Search_Precision_Type_Enum.Synonmic_Form:
                                // If there are any non-characters, don't use thesaurus for this term
                                bool thesaurus = db_terms[i].All(Char.IsLetter);
                                if (thesaurus)
                                {
                                    db_terms[i] = "FORMSOF(thesaurus," + db_terms[i] + ")";
                                }
                                else
                                {
                                    db_terms[i] = "\"" + db_terms[i] + "\"";
                                }
                                break;
                        }
                    }
                    else
                    {
                        if (Current_Mode.Search_Precision != Search_Precision_Type_Enum.Exact_Match)
                        {
                            db_terms[i] = "\"" + db_terms[i] + "\"";
                        }
                    }
                }
            }

            // Get the page count in the results
            int current_page_index =Current_Mode.Page;

            // If this is an exact match, just do the search
            if (Current_Mode.Search_Precision == Search_Precision_Type_Enum.Exact_Match)
            {
                Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Exact_Search_Paged(db_terms[0], db_fields[0], INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                if (Need_Search_Statistics)
                    Complete_Result_Set_Info = returnArgs.Statistics;
                Paged_Results = returnArgs.Paged_Results;
            }
            else
            {
                // Finish filling up the fields and links
                while (links.Count < 10)
                    links.Add(0);
                while (db_fields.Count < 10)
                    db_fields.Add(-1);
                while (db_terms.Count < 10)
                    db_terms.Add(String.Empty);

                // See if this is a simple search, which can use a more optimized search routine
                bool simplified_search = db_fields.All(Field => (Field <= 0));

                // Perform either the simpler metadata search, or the more complex
                if (simplified_search)
                {
                    StringBuilder searchBuilder = new StringBuilder();
                    for (int i = 0; i < db_terms.Count; i++)
                    {
                        if (db_terms[i].Length > 0)
                        {
                            if (i > 0)
                            {
                                if (i > links.Count)
                                {
                                    searchBuilder.Append(" AND ");
                                }
                                else
                                {
                                    switch (links[i - 1])
                                    {
                                        case 0:
                                            searchBuilder.Append(" AND ");
                                            break;

                                        case 1:
                                            searchBuilder.Append(" OR ");
                                            break;

                                        case 2:
                                            searchBuilder.Append(" AND NOT ");
                                            break;
                                    }
                                }
                            }

                            searchBuilder.Append(db_terms[i]);
                        }
                    }

                    Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Search_Paged(searchBuilder.ToString(), INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                    if (Need_Search_Statistics)
                        Complete_Result_Set_Info = returnArgs.Statistics;
                    Paged_Results = returnArgs.Paged_Results;
                }
                else
                {
                    // Perform search in the database
                    Multiple_Paged_Results_Args returnArgs = Engine_Database.Perform_Metadata_Search_Paged(links[0], db_terms[0], db_fields[0], links[1], db_terms[1], db_fields[1], links[2], db_terms[2], db_fields[2], links[3], db_terms[3],
                                                                                                            db_fields[3], links[4], db_terms[4], db_fields[4], links[5], db_terms[5], db_fields[5], links[6], db_terms[6], db_fields[6], links[7], db_terms[7], db_fields[7], links[8], db_terms[8], db_fields[8],
                                                                                                            links[9], db_terms[9], db_fields[9], INCLUDE_PRIVATE, Current_Mode.Aggregation, Date1, Date2, Results_Per_Page, current_page_index, Current_Sort, Need_Search_Statistics, facetsList, Need_Search_Statistics, Tracer);
                    if (Need_Search_Statistics)
                        Complete_Result_Set_Info = returnArgs.Statistics;
                    Paged_Results = returnArgs.Paged_Results;
                }
            }
        }