Respresents the Package-Search-Params format in the CKAN REST API.
        /// <summary>
        /// Provides a view of features packages that have recently bee updated in the repository group.
        /// Return the three most recently updated packages in the group.
        /// </summary>
        public ActionResult RecentlyUpdated()
        {
            log.DebugFormat("Controller action requested");

            // Create the CKAN search parameters
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());

            // Collect the results
            List<Package> packages = CkanHelper.GetAllPackages();

            // Sort by date and take the top 3
            packages = (from package in packages
                                      orderby package.MetadataModified
                                      descending
                                      select package)
                         .Take(3)
                         .ToList();

            SettingsHelper.FilterTitles(packages);

            // Update the results to the top 3
            PackageSearchResponse<Package> response = new PackageSearchResponse<Package>();
            response.Results = packages;
            response.Count = packages.Count;

            // Populate the model
            PackageSearchResultsModel model = new PackageSearchResultsModel();
            model.SearchResults = response;

            // Render the view
            return View(model);
        }
        /// <summary>
        /// Get the number of packages in the CKAN group.
        /// </summary>
        /// <returns></returns>
        public static int GetPackageCount()
        {
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());
            searchParameters.Limit = 0;

            int count = CkanHelper.GetClient().SearchPackages<string>(searchParameters, new CacheSettings(SettingsHelper.GetPackageCountCacheDuration(), SettingsHelper.GetPackageCountCacheBackgroundUpdate())).Count;
            return count;
        }
        public void ShouldReturnPackagesWithTag()
        {
            CkanClient client = CkanApiHelper.GetCkanClient();

            PackageSearchParameters parameters = new PackageSearchParameters();
            parameters.Tags.Add("colorado");

            PackageSearchResponse<Package> response = client.SearchPackages<Package>(parameters);

            Assert.NotEmpty(response.Results);
        }
        public void ShouldReturnPackageIdsWithQuery()
        {
            CkanClient client = CkanApiHelper.GetCkanClient();

            PackageSearchParameters parameters = new PackageSearchParameters();
            parameters.Query = "bike";

            PackageSearchResponse<string> response = client.SearchPackages<string>(parameters);

            Assert.NotEmpty(response.Results);
        }
        public void ShouldReturnSinglePackagesWithQueryLimit()
        {
            CkanClient client = CkanApiHelper.GetCkanClient();

            PackageSearchParameters parameters = new PackageSearchParameters();
            parameters.Query = "bike";
            parameters.Offset = 0;
            parameters.Limit = 1;

            PackageSearchResponse<Package> response = client.SearchPackages<Package>(parameters);

            Assert.True(response.Results.Count == 1);
        }
        /// <summary>
        /// Get all packages from the CKAN group.
        /// </summary>
        /// <returns></returns>
        public static List<Package> GetAllPackages()
        {
            CacheSettings settings = new CacheSettings(SettingsHelper.GetAllPackagesCacheDuration(), SettingsHelper.GetAllPackagesCacheBackgroundUpdate());

            // Create the CKAN search parameters
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());
            searchParameters.Limit = GetPackageCount();

            // Collect the results
            PackageSearchResponse<Package> response = CkanHelper.GetClient().SearchPackages<Package>(searchParameters, settings);

            return response.Results;
        }
        public void ShouldReturnPackagesWithGroup()
        {
            CkanClient client = CkanApiHelper.GetCkanClient();

            PackageSearchParameters parameters = new PackageSearchParameters();
            parameters.Groups.Add("arvada");

            PackageSearchResponse<Package> response = client.SearchPackages<Package>(parameters);

            Assert.NotEmpty(response.Results);

            foreach (var result in response.Results)
            {
                Assert.Contains<string>("arvada", result.Groups);
            }
        }
        /// <summary>
        /// Provides a view of features packages in the repository group.  Featured
        /// packages are those that are tagged with "featured".
        /// </summary>
        public ActionResult FeaturedPackages()
        {
            log.DebugFormat("Controller action requested");

            // Create the CKAN search parameters
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());
            searchParameters.Limit = SettingsHelper.GetHomeFeaturedPackageLimit();
            searchParameters.Tags.Add(SettingsHelper.GetHomeFeaturedPackagesTag());

            // Collect the results
            PackageSearchResponse<Package> results = CkanHelper.GetClient().SearchPackages<Package>(searchParameters, new CacheSettings(SettingsHelper.GetFeaturedPackagesCacheDuration()));
            SettingsHelper.FilterTitles(results.Results);

            // Render the view
            return View(results);
        }
        /// <summary>
        /// Get all tags from the CKAN group.
        /// </summary>
        /// <returns></returns>
        public static List<Tag> GetAllTags(int limit)
        {
            CacheSettings settings = new CacheSettings(SettingsHelper.GetAllPackagesCacheDuration(), SettingsHelper.GetAllPackagesCacheBackgroundUpdate());

            // Create the CKAN search parameters
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());

            // Collect the results
            List<Tag> tags = CkanHelper.GetClient().SearchPackagesGetTagCounts(searchParameters, settings);

            // Remove filtered tags
            tags = TagHelper.FilterTags(tags);

            if (limit > 0)
            {
                tags = TagHelper.LimitTags(tags, limit);
            }

            return tags;
        }
        /// <summary>
        /// Generates a trie of suggestions based on attributes of packages in the catalog
        /// </summary>
        /// <param name="client"></param>
        /// <param name="searchParameters"></param>
        /// <param name="cacheSettings"></param>
        /// <returns></returns>
        private static Trie GenerateTrie(CkanClient client, PackageSearchParameters searchParameters, CacheSettings cacheSettings)
        {
            // Create an empty trie
            Trie trie = new Trie();

            // Run the search to get all packages
            PackageSearchResponse<Package> response = client.SearchPackages<Package>(searchParameters, cacheSettings);

            // Add the entrys to the trie
            foreach (var package in response.Results)
	        {
                // Add the package title
                trie.Add(package.Title);

                // Add the package tags (removing hyphens which represent spaces)
                foreach (var tag in package.Tags)
                {
                    trie.Add(tag.Replace("-"," "));
                }
	        }

            return trie;
        }
        /// <summary>
        /// Gets a trie of package search suggestions a package search.  
        /// If cached, the trie will be returned from the 
        /// cache, otherwise it will be created and cached.
        /// </summary>
        /// <param name="client"></param>
        /// <param name="searchParameters"></param>
        /// <param name="cacheSettings"></param>
        /// <returns></returns>
        public static Trie GetTrie(CkanClient client, PackageSearchParameters searchParameters, CacheSettings cacheSettings)
        {
            Trie trie = null;

            // Get the trie from the cache
            string key = CkanClient.CacheKeyPrefix + "AutoComplete";

            // Get the memorycache
            MemoryCache cache = MemoryCache.Default;

            // Get the cached entry if it exists
            CacheEntry cacheEntry = cache[key] as CacheEntry;
            if (cacheEntry == null) {
                // Generate the trie
                trie = GenerateTrie(client, searchParameters, cacheSettings);

                cacheEntry = new CacheEntry();
                cacheEntry.Data = trie;
                cacheEntry.Label = "Autocomplete index";
                cacheEntry.LastCached = DateTime.Now;
                cacheEntry.Duration = cacheSettings.Duration;

                CacheItemPolicy policy = new CacheItemPolicy();
                policy.AbsoluteExpiration = DateTimeOffset.Now.Add(cacheSettings.Duration);

                // Add the trie to the cache
                cache.Set(key, cacheEntry, policy);
            }
            else
            {
                // Get the trie from the cache
                trie = (Trie)cacheEntry.Data;
            }

            return trie;
        }
        public ActionResult Index(string q, int? page, string order_by, string tag, string mode)
        {
            log.DebugFormat("Controller action requested");

            this.ConfigureBreadCrumbs(q, tag);

            // Create the CKAN search parameters
            var searchParameters = new PackageSearchParameters();
            searchParameters.AggregateTagCounts = true;
            searchParameters.Query = q;
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());
            
            // Ordering
            if (!String.IsNullOrEmpty(order_by))
            {
                searchParameters.OrderBy = order_by;
            }

            // Tag
            if (!String.IsNullOrEmpty(tag))
            {
                searchParameters.Tags.Add(tag);
            }

            // Build the view model for the results
            PackageSearchResultsModel model = new PackageSearchResultsModel();
            Pager pager = null;

            if (mode == "table")
            {
                model.DisplayMode = ResultsDisplayMode.Table;
                searchParameters.Offset = 0;
                // TODO: In the table mode all results are currently returned and paginated client side.
                // This isn't scalable so this will need looked at in the future (AJAX pagination maybe?)
                searchParameters.Limit = CkanHelper.GetPackageCount();
            }
            else // mode == list (default)
            {
                // Set up the pagination
                int pageNumber = 1;
                if (page != null)
                {
                    pageNumber = (int)page;
                }

                pager = new Pager(pageNumber, SettingsHelper.GetSearchResultsPerPage());

                searchParameters.Offset = pager.RecordOffset;
                searchParameters.Limit = pager.RecordsPerPage;
            }

            // Execute the search
            model.SearchParameters = searchParameters;
            model.SearchResults = CkanHelper.GetClient().SearchPackages<Package>(searchParameters, new CacheSettings(SettingsHelper.GetSearchResultsCacheDuration()));
            
            // Filter the titles
            SettingsHelper.FilterTitles(model.SearchResults.Results);

            // Filter the tags
            model.SearchResults.Tags = TagHelper.FilterTags(model.SearchResults.Tags);

            // Set the pager if we are using it
            if (pager != null)
            {
                // Set the number of records to be paged
                pager.RecordCount = model.SearchResults.Count;      

                // Add the pager to the model
                model.Pager = pager;
            }

            // Render the view
            return View(model);
        }
        /// <summary>
        /// Provides a view of features packages that have recently bee updated in the repository group.
        /// Return the three most recently updated packages in the group.
        /// </summary>
        public ActionResult BrowsePackages()
        {
            log.DebugFormat("Controller action requested");

            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());

            // Set up the pagination
            Pager pager = new Pager(1, SettingsHelper.GetSearchResultsPerPage());
            searchParameters.Offset = pager.RecordOffset;
            searchParameters.Limit = pager.RecordsPerPage;

            PackageSearchResultsModel model = new PackageSearchResultsModel();
            model.SearchParameters = searchParameters;
            model.SearchResults = CkanHelper.GetClient().SearchPackages<Package>(searchParameters, new CacheSettings(SettingsHelper.GetSearchResultsCacheDuration()));
            SettingsHelper.FilterTitles(model.SearchResults.Results);
            model.Pager = pager;

            // Set the number of records to be paged
            pager.RecordCount = model.SearchResults.Count;

            // Render the view
            return View(model);
        }
        /// <summary>
        /// Get search suggestions for all packages in the catalog group.  Uses the all packages cache
        /// to build a search index that is also cached for performance.
        /// </summary>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public static List<string> GetPackageSearchSuggestions(string prefix)
        {
            CacheSettings settings = new CacheSettings(SettingsHelper.GetAllPackagesCacheDuration(), SettingsHelper.GetAllPackagesCacheBackgroundUpdate());

            // Create the CKAN search parameters to get all packages in the group
            var searchParameters = new PackageSearchParameters();
            searchParameters.Groups.Add(SettingsHelper.GetCatalogGroup());
            searchParameters.Limit = GetPackageCount();

            // Get a list of search suggestions
            List<string> suggestions = CkanHelper.GetClient().GetPackageSearchSuggestions(prefix, searchParameters, settings);

            return suggestions;
        }