// ..................................................................................Get Product Filters.....................................................................
        public async Task <IEnumerable <FilterData> > GetProductFilters(QueryParams queryParams, IEnumerable <ProductDTO> products)
        {
            List <FilterData>         filters = new List <FilterData>();
            List <IQueryFilterOption> options = new List <IQueryFilterOption>();

            // ******Price Filter********
            if (!queryParams.Filters.Any(x => x.Key == "Price"))
            {
                // Cross join between products and the priceRanges table to get the price range options
                var crossJoin =
                    from pr in await context.PriceRanges.ToListAsync()
                    from p in products
                    orderby pr.Id
                        where (p.MinPrice >= pr.Min && p.MinPrice < pr.Max) || (p.MaxPrice > 0 && pr.Min >= p.MinPrice && pr.Min < p.MaxPrice)
                    select(IQueryFilterOption) new PriceFilterOption
                {
                    Min   = pr.Min,
                    Max   = pr.Max,
                    Label = "$" + pr.Min + " - $" + pr.Max
                };

                options = crossJoin.Distinct().ToList();
            }


            // Create the filter data object and add it to the filters
            FilterData filterData = new FilterData
            {
                Type    = "Price",
                Caption = "Price",
                Options = options
            };

            filters.Add(filterData);



            // ******Rating Filter********
            List <IQueryFilterOption> ratingOptions  = new List <IQueryFilterOption>();
            IEnumerable <double>      productRatings = products.Select(x => x.Rating).ToList();


            // Check to see if we have a customer rating filter selected
            if (queryParams.Filters.Any(x => x.Key == "Customer Rating"))
            {
                // Remove the customer rating filter and query without it
                // This is so we can get other rating filters that belong in the results
                queryParams.Filters.RemoveAll(x => x.Key == "Customer Rating");
                productRatings = await GetProductRatings(queryParams);
            }


            // Rating 4 and up
            if (productRatings.Count(x => x >= 4) > 0)
            {
                ratingOptions.Add(new RatingOption
                {
                    Id = "4"
                });
            }



            // Rating 3 and up
            if (productRatings.Count(x => x >= 3 && x < 4) > 0)
            {
                ratingOptions.Add(new RatingOption
                {
                    Id = "3"
                });
            }



            // Rating 2 and up
            if (productRatings.Count(x => x >= 2 && x < 3) > 0)
            {
                ratingOptions.Add(new RatingOption
                {
                    Id = "2"
                });
            }



            // Rating 1 and up
            if (productRatings.Count(x => x >= 1 && x < 2) > 0)
            {
                ratingOptions.Add(new RatingOption
                {
                    Id = "1"
                });
            }



            if (ratingOptions.Count > 0)
            {
                filterData = new FilterData
                {
                    Type    = "Rating",
                    Caption = "Customer Rating",
                    Options = ratingOptions
                };

                filters.Add(filterData);
            }



            // ******Custom Filters********

            // Get all the filter option Ids that are related to the product Ids
            List <int> filterOptionIds = await context.ProductFilters
                                         .AsNoTracking()
                                         .Where(x => products.Select(y => y.Id)
                                                .Contains(x.ProductId))
                                         .Select(x => x.FilterOptionId)
                                         .Distinct()
                                         .ToListAsync();



            // Display the other options for a selected filter
            foreach (KeyValuePair <string, string> customFilter in queryParams.CustomFilters)
            {
                List <int> optionsIds;

                // Exclude the current option from the list of options and query products
                queryParams.SetCustomFilterOptions(customFilter.Key);
                var pIds = await GetProductIds(queryParams);

                // Get a list of option ids that we can display from the current custom filter
                optionsIds = await context.ProductFilters
                             .AsNoTracking()
                             .Where(x => pIds
                                    .Contains(x.ProductId) && x.FilterOption.Filter.Name == customFilter.Key)
                             .Select(x => x.FilterOptionId).Distinct()
                             .ToListAsync();

                // Add the new option ids to the list
                filterOptionIds = filterOptionIds
                                  .Concat(optionsIds)
                                  .ToList();
            }


            // Get the raw filter data consisting of filter name and filter option name
            var rawFilterData = await context.FilterOptions
                                .AsNoTracking()
                                .OrderBy(x => x.FilterId)
                                .Where(x => filterOptionIds
                                       .Contains(x.Id))
                                .Select(x => new
            {
                filterName = x.Filter.Name,
                optionName = x.Name,
                optionId   = x.Id
            })
                                .ToListAsync();



            // Format the raw data into a list of filter data
            List <FilterData> filterDataList = rawFilterData
                                               .GroupBy(x => x.filterName)
                                               .Select(x => new FilterData
            {
                Type    = "Custom",
                Caption = x.Select(y => y.filterName).FirstOrDefault(),
                Options = x.Select(y => (IQueryFilterOption) new QueryFilterOption
                {
                    Id    = y.optionId.ToString(),
                    Label = y.optionName
                })
                          .ToList()
            })
                                               .ToList();

            // Add the filters
            filters.AddRange(filterDataList);


            return(filters);
        }