private IEnumerable<moduleModel.CatalogProduct> Search(CatalogIndexedSearchCriteria criteria, out CatalogItemSearchResults results, moduleModel.ItemResponseGroup responseGroup)
        {
            var items = new List<moduleModel.CatalogProduct>();
            var itemsOrderedList = new List<string>();

            var foundItemCount = 0;
            var dbItemCount = 0;
            var searchRetry = 0;

            //var myCriteria = criteria.Clone();
            var myCriteria = criteria;

            do
            {
                // Search using criteria, it will only return IDs of the items
                var scope = _searchConnection.Scope;
                var searchResults = _searchProvider.Search(scope, criteria) as SearchResults;
                var itemKeyValues = searchResults.GetKeyAndOutlineFieldValueMap<string>();
                results = new CatalogItemSearchResults(myCriteria, itemKeyValues, searchResults);

                searchRetry++;

                if (results.Items == null)
                {
                    continue;
                }

                //Get only new found itemIds
                var uniqueKeys = results.Items.Keys.Except(itemsOrderedList).ToArray();
                foundItemCount = uniqueKeys.Length;

                if (!results.Items.Any())
                {
                    continue;
                }

                itemsOrderedList.AddRange(uniqueKeys);

                // Now load items from repository
                var currentItems = _itemService.GetByIds(uniqueKeys.ToArray(), responseGroup);

                var orderedList = currentItems.OrderBy(i => itemsOrderedList.IndexOf(i.Id));
                items.AddRange(orderedList);
                dbItemCount = currentItems.Length;

                //If some items where removed and search is out of sync try getting extra items
                if (foundItemCount > dbItemCount)
                {
                    //Retrieve more items to fill missing gap
                    myCriteria.RecordsToRetrieve += (foundItemCount - dbItemCount);
                }
            }
            while (foundItemCount > dbItemCount && results.Items.Any() && searchRetry <= 3 &&
                (myCriteria.RecordsToRetrieve + myCriteria.StartingRecord) < results.TotalCount);

            return items;
        }
        public void Can_find_item_lucene()
        {
            var scope = "default";
            var queryBuilder = new LuceneSearchQueryBuilder();
            var conn = new SearchConnection(_LuceneStorageDir, scope);
            var provider = new LuceneSearchProvider(queryBuilder, conn);

            if (Directory.Exists(_LuceneStorageDir))
            {
                Directory.Delete(_LuceneStorageDir, true);
            }

            SearchHelper.CreateSampleIndex(provider, scope);

            var criteria = new CatalogIndexedSearchCriteria
            {
                SearchPhrase = "product",
                IsFuzzySearch = true,
                Catalog = "goods",
                RecordsToRetrieve = 10,
                StartingRecord = 0,
                Pricelists = new string[] { }
            };


            var results = provider.Search(scope, criteria);

            Assert.True(results.DocCount == 1, String.Format("Returns {0} instead of 1", results.DocCount));

            criteria = new CatalogIndexedSearchCriteria
            {
                SearchPhrase = "sample product ",
                IsFuzzySearch = true,
                Catalog = "goods",
                RecordsToRetrieve = 10,
                StartingRecord = 0,
                Pricelists = new string[] { }
            };


            results = provider.Search(scope, criteria);

            Assert.True(results.DocCount == 1, String.Format("\"Sample Product\" search returns {0} instead of 1", results.DocCount));

            Directory.Delete(_LuceneStorageDir, true);
        }
        public moduleModel.SearchResult SearchItems(CatalogIndexedSearchCriteria criteria, moduleModel.ItemResponseGroup responseGroup)
        {
            CatalogItemSearchResults results;
            var items = Search(criteria, out results, responseGroup);

            var response = new moduleModel.SearchResult();

            response.Products.AddRange(items);
            response.ProductsTotalCount = results.TotalCount;

            // TODO need better way to find applied filter values
            var appliedFilters = criteria.CurrentFilters.SelectMany(x => x.GetValues()).Select(x => x.Id).ToArray();
            if (results.FacetGroups != null)
            {
                response.Aggregations = results.FacetGroups.Select(g => g.ToModuleModel(appliedFilters)).ToArray();
            }
            return response;
        }
        public ProductSearchResult SearchItems(CatalogIndexedSearchCriteria criteria, moduleModel.ItemResponseGroup responseGroup = moduleModel.ItemResponseGroup.ItemSmall)
        {
            CatalogItemSearchResults results;
            var items = Search(criteria, out results, responseGroup);
            var catalogItems = new List<Product>();

            // go through items
            foreach (var item in items)
            {
                var searchTags = results.Items[item.Id.ToLower()];

                var currentOutline = this.GetItemOutlineUsingContext(
                    searchTags[criteria.OutlineField].ToString(),
                    criteria.Catalog);
                var catalogItem = item.ToWebModel(_blobUrlResolver) as Product;
                catalogItem.Outline = this.StripCatalogFromOutline(currentOutline, criteria.Catalog);

                int reviewTotal;
                if (searchTags.ContainsKey(criteria.ReviewsTotalField)
                    && int.TryParse(searchTags[criteria.ReviewsTotalField].ToString(), out reviewTotal))
                {
                    catalogItem.ReviewsTotal = reviewTotal;
                }
                double reviewAvg;
                if (searchTags.ContainsKey(criteria.ReviewsAverageField)
                    && double.TryParse(searchTags[criteria.ReviewsAverageField].ToString(), out reviewAvg))
                {
                    catalogItem.Rating = reviewAvg;
                }

                catalogItems.Add(catalogItem);
            }

            var response = new ProductSearchResult();

            response.Items.AddRange(catalogItems);
            response.TotalCount = results.TotalCount;

            //TODO need better way to find applied filter values
            var appliedFilters = criteria.CurrentFilters.SelectMany(x => x.GetValues()).Select(x => x.Id).ToArray();
            response.Facets = results.FacetGroups == null ? null : results.FacetGroups.Select(g => g.ToWebModel(appliedFilters)).ToArray();
            return response;
        }
		public IHttpActionResult Search(string store, string[] priceLists, [ModelBinder(typeof(SearchParametersBinder))] SearchParameters parameters,
										[FromUri] coreModel.ItemResponseGroup responseGroup = coreModel.ItemResponseGroup.ItemMedium,
										[FromUri] string outline = "", string language = "en-us", string currency = "USD")
		{
			var context = new Dictionary<string, object>
                          {
                              { "StoreId", store },
                          };

			var fullLoadedStore = GetStoreById(store);
			if (fullLoadedStore == null)
			{
				throw new NullReferenceException(store + " not found");
			}

			var catalog = fullLoadedStore.Catalog;

			string categoryId = null;

			var criteria = new CatalogIndexedSearchCriteria { Locale = language, Catalog = catalog.ToLowerInvariant() };

			if (!string.IsNullOrWhiteSpace(outline))
			{
				criteria.Outlines.Add(String.Format("{0}/{1}*", catalog, outline));
				categoryId = outline.Split(new[] { '/' }).Last();
				context.Add("CategoryId", categoryId);
            }

            #region Filters
            // Now fill in filters
			var filters = _browseFilterService.GetFilters(context);

			// Add all filters
			foreach (var filter in filters)
			{
				criteria.Add(filter);
			}

			// apply terms
			if (parameters.Terms != null && parameters.Terms.Count > 0)
			{
				foreach (var term in parameters.Terms)
				{
					var filter = filters.SingleOrDefault(x => x.Key.Equals(term.Key, StringComparison.OrdinalIgnoreCase)
						&& (!(x is PriceRangeFilter) || ((PriceRangeFilter)x).Currency.Equals(currency, StringComparison.OrdinalIgnoreCase)));

					var appliedFilter = _browseFilterService.Convert(filter, term.Value);

					criteria.Apply(appliedFilter);
				}
            }
            #endregion

            #region Facets
            // apply facet filters
			var facets = parameters.Facets;
			if (facets.Count != 0)
			{
				foreach (var key in facets.Keys)
				{
					var filter = filters.SingleOrDefault(
						x => x.Key.Equals(key, StringComparison.OrdinalIgnoreCase)
							&& (!(x is PriceRangeFilter)
								|| ((PriceRangeFilter)x).Currency.Equals(currency, StringComparison.OrdinalIgnoreCase)));

					var appliedFilter = _browseFilterService.Convert(filter, facets[key]);
					criteria.Apply(appliedFilter);
				}
            }
            #endregion

            //criteria.ClassTypes.Add("Product");
			criteria.RecordsToRetrieve = parameters.PageSize == 0 ? 10 : parameters.PageSize;
			criteria.StartingRecord = parameters.StartingRecord;
			criteria.Pricelists = priceLists;
			criteria.Currency = currency;
			criteria.StartDateFrom = parameters.StartDateFrom;
			criteria.SearchPhrase = parameters.FreeSearch;

			#region sorting

			if (!string.IsNullOrEmpty(parameters.Sort))
			{
				var isDescending = "desc".Equals(parameters.SortOrder, StringComparison.OrdinalIgnoreCase);

				SearchSort sortObject = null;

				switch (parameters.Sort.ToLowerInvariant())
				{
					case "price":
						if (criteria.Pricelists != null)
						{
							sortObject = new SearchSort(
								criteria.Pricelists.Select(
									priceList =>
										new SearchSortField(String.Format("price_{0}_{1}", criteria.Currency.ToLower(), priceList.ToLower()))
										{
											IgnoredUnmapped = true,
											IsDescending = isDescending,
											DataType = SearchSortField.DOUBLE
										})
									.ToArray());
						}
						break;
					case "position":
						sortObject =
							new SearchSort(
								new SearchSortField(string.Format("sort{0}{1}", catalog, categoryId).ToLower())
								{
									IgnoredUnmapped = true,
									IsDescending = isDescending
								});
						break;
					case "name":
						sortObject = new SearchSort("name", isDescending);
						break;
					case "rating":
						sortObject = new SearchSort(criteria.ReviewsAverageField, isDescending);
						break;
					case "reviews":
						sortObject = new SearchSort(criteria.ReviewsTotalField, isDescending);
						break;
					default:
						sortObject = CatalogIndexedSearchCriteria.DefaultSortOrder;
						break;
				}

				criteria.Sort = sortObject;
			}

			#endregion

			//Load ALL products 
			var searchResults = _browseService.SearchItems(criteria, responseGroup);

            // populate inventory
		    if ((responseGroup & ItemResponseGroup.ItemProperties) == ItemResponseGroup.ItemProperties)
		    {
                PopulateInventory(fullLoadedStore.FulfillmentCenter, searchResults.Items);
		    }

		    return this.Ok(searchResults);
		}
        public IHttpActionResult Search([FromUri] ProductSearchRequest request)
        {
            request = request ?? new ProductSearchRequest();
            request.Normalize();

            var context = new Dictionary<string, object>
            {
                { "StoreId", request.Store },
            };

            var fullLoadedStore = GetStoreById(request.Store);
            if (fullLoadedStore == null)
            {
                throw new NullReferenceException(request.Store + " not found");
            }

            var catalog = fullLoadedStore.Catalog;

            string categoryId = null;

            var criteria = new CatalogIndexedSearchCriteria { Locale = request.Language, Catalog = catalog.ToLowerInvariant(), IsFuzzySearch = true };

            if (!string.IsNullOrWhiteSpace(request.Outline))
            {
                criteria.Outlines.Add(string.Format("{0}/{1}*", catalog, request.Outline));
                categoryId = request.Outline.Split(new[] { '/' }).Last();
                context.Add("CategoryId", categoryId);
            }

            #region Filters
            // Now fill in filters
            var filters = _browseFilterService.GetFilters(context);

            // Add all filters
            foreach (var filter in filters)
            {
                criteria.Add(filter);
            }

            // apply terms
            var terms = ParseKeyValues(request.Terms);
            if (terms.Any())
            {
                var filtersWithValues = filters
                    .Where(x => (!(x is PriceRangeFilter) || ((PriceRangeFilter)x).Currency.Equals(request.Currency, StringComparison.OrdinalIgnoreCase)))
                    .Select(x => new { Filter = x, Values = x.GetValues() })
                    .ToList();

                foreach (var term in terms)
                {
                    var filter = filters.SingleOrDefault(x => x.Key.Equals(term.Key, StringComparison.OrdinalIgnoreCase)
                        && (!(x is PriceRangeFilter) || ((PriceRangeFilter)x).Currency.Equals(request.Currency, StringComparison.OrdinalIgnoreCase)));

                    // handle special filter term with a key = "tags", it contains just values and we need to determine which filter to use
                    if (filter == null && term.Key == "tags")
                    {
                        foreach (var termValue in term.Values)
                        {
                            // try to find filter by value
                            var foundFilter = filtersWithValues.FirstOrDefault(x => x.Values.Any(y => y.Id.Equals(termValue)));

                            if (foundFilter != null)
                            {
                                filter = foundFilter.Filter;

                                var appliedFilter = _browseFilterService.Convert(filter, term.Values);
                                criteria.Apply(appliedFilter);
                            }
                        }
                    }
                    else
                    {
                        var appliedFilter = _browseFilterService.Convert(filter, term.Values);
                        criteria.Apply(appliedFilter);
                    }
                }
            }
            #endregion

            #region Facets
            // apply facet filters
            var facets = ParseKeyValues(request.Facets);
            foreach (var facet in facets)
            {
                var filter = filters.SingleOrDefault(
                    x => x.Key.Equals(facet.Key, StringComparison.OrdinalIgnoreCase)
                        && (!(x is PriceRangeFilter)
                            || ((PriceRangeFilter)x).Currency.Equals(request.Currency, StringComparison.OrdinalIgnoreCase)));

                var appliedFilter = _browseFilterService.Convert(filter, facet.Values);
                criteria.Apply(appliedFilter);
            }
            #endregion

            //criteria.ClassTypes.Add("Product");
            criteria.RecordsToRetrieve = request.Take <= 0 ? 10 : request.Take;
            criteria.StartingRecord = request.Skip;
            criteria.Pricelists = request.Pricelists;
            criteria.Currency = request.Currency;
            criteria.StartDateFrom = request.StartDateFrom;
            criteria.SearchPhrase = request.SearchPhrase;

            #region sorting

            if (!string.IsNullOrEmpty(request.Sort))
            {
                var isDescending = "desc".Equals(request.SortOrder, StringComparison.OrdinalIgnoreCase);

                SearchSort sortObject = null;

                switch (request.Sort.ToLowerInvariant())
                {
                    case "price":
                        if (criteria.Pricelists != null)
                        {
                            sortObject = new SearchSort(
                                criteria.Pricelists.Select(
                                    priceList =>
                                        new SearchSortField(String.Format("price_{0}_{1}", criteria.Currency.ToLower(), priceList.ToLower()))
                                        {
                                            IgnoredUnmapped = true,
                                            IsDescending = isDescending,
                                            DataType = SearchSortField.DOUBLE
                                        })
                                    .ToArray());
                        }
                        break;
                    case "position":
                        sortObject =
                            new SearchSort(
                                new SearchSortField(string.Format("sort{0}{1}", catalog, categoryId).ToLower())
                                {
                                    IgnoredUnmapped = true,
                                    IsDescending = isDescending
                                });
                        break;
                    case "name":
                        sortObject = new SearchSort("name", isDescending);
                        break;
                    case "rating":
                        sortObject = new SearchSort(criteria.ReviewsAverageField, isDescending);
                        break;
                    case "reviews":
                        sortObject = new SearchSort(criteria.ReviewsTotalField, isDescending);
                        break;
                    default:
                        sortObject = CatalogIndexedSearchCriteria.DefaultSortOrder;
                        break;
                }

                criteria.Sort = sortObject;
            }

            #endregion

            //Load ALL products 
            var searchResults = _browseService.SearchItems(criteria, request.ResponseGroup);

            // populate inventory
            if ((request.ResponseGroup & ItemResponseGroup.ItemProperties) == ItemResponseGroup.ItemProperties)
            {
                PopulateInventory(fullLoadedStore.FulfillmentCenter, searchResults.Items);
            }

            return Ok(searchResults);
        }
        private SearchResult SearchProducts(SearchCriteria criteria)
        {
            var context = new Dictionary<string, object>
            {
                { "StoreId", criteria.StoreId },
            };

            var store = _storeService.GetById(criteria.StoreId);
            if (store == null)
            {
                throw new NullReferenceException("Cannot find store '" + criteria.StoreId + "'");
            }

            var catalog = store.Catalog;
            var categoryId = criteria.CategoryId;

            var serviceCriteria = new CatalogIndexedSearchCriteria
            {
                Locale = criteria.LanguageCode,
                Catalog = catalog.ToLowerInvariant(),
                IsFuzzySearch = true,
            };

            if (!string.IsNullOrWhiteSpace(criteria.Outline))
            {
                serviceCriteria.Outlines.Add(string.Format(CultureInfo.InvariantCulture, "{0}/{1}*", catalog, criteria.Outline));
                categoryId = criteria.Outline.Split('/').Last();
            }
            else
            {
                if (!string.IsNullOrEmpty(categoryId))
                {
                    serviceCriteria.Outlines.Add(string.Format(CultureInfo.InvariantCulture, "{0}/{1}*", catalog, categoryId));
                }
            }

            if (!string.IsNullOrEmpty(categoryId))
            {
                context.Add("CategoryId", categoryId);
            }

            #region Filters
            // Now fill in filters
            var filters = _browseFilterService.GetFilters(context);

            // Add all filters
            foreach (var filter in filters)
            {
                serviceCriteria.Add(filter);
            }

            // apply terms
            var terms = ParseKeyValues(criteria.Terms);
            if (terms.Any())
            {
                var filtersWithValues = filters
                    .Where(x => (!(x is PriceRangeFilter) || ((PriceRangeFilter)x).Currency.Equals(criteria.Currency, StringComparison.OrdinalIgnoreCase)))
                    .Select(x => new { Filter = x, Values = x.GetValues() })
                    .ToList();

                foreach (var term in terms)
                {
                    var filter = filters.SingleOrDefault(x => x.Key.Equals(term.Key, StringComparison.OrdinalIgnoreCase)
                        && (!(x is PriceRangeFilter) || ((PriceRangeFilter)x).Currency.Equals(criteria.Currency, StringComparison.OrdinalIgnoreCase)));

                    // handle special filter term with a key = "tags", it contains just values and we need to determine which filter to use
                    if (filter == null && term.Key == "tags")
                    {
                        foreach (var termValue in term.Values)
                        {
                            // try to find filter by value
                            var foundFilter = filtersWithValues.FirstOrDefault(x => x.Values.Any(y => y.Id.Equals(termValue)));

                            if (foundFilter != null)
                            {
                                filter = foundFilter.Filter;

                                var appliedFilter = _browseFilterService.Convert(filter, term.Values);
                                serviceCriteria.Apply(appliedFilter);
                            }
                        }
                    }
                    else
                    {
                        var appliedFilter = _browseFilterService.Convert(filter, term.Values);
                        serviceCriteria.Apply(appliedFilter);
                    }
                }
            }
            #endregion

            #region Facets
            // apply facet filters
            var facets = ParseKeyValues(criteria.Facets);
            foreach (var facet in facets)
            {
                var filter = filters.SingleOrDefault(
                    x => x.Key.Equals(facet.Key, StringComparison.OrdinalIgnoreCase)
                        && (!(x is PriceRangeFilter)
                            || ((PriceRangeFilter)x).Currency.Equals(criteria.Currency, StringComparison.OrdinalIgnoreCase)));

                var appliedFilter = _browseFilterService.Convert(filter, facet.Values);
                serviceCriteria.Apply(appliedFilter);
            }
            #endregion

            //criteria.ClassTypes.Add("Product");
            serviceCriteria.RecordsToRetrieve = criteria.Take <= 0 ? 10 : criteria.Take;
            serviceCriteria.StartingRecord = criteria.Skip;
            serviceCriteria.Pricelists = criteria.PricelistIds;
            serviceCriteria.Currency = criteria.Currency;
            serviceCriteria.StartDateFrom = criteria.StartDateFrom;
            serviceCriteria.SearchPhrase = criteria.Keyword;

            #region sorting

            if (!string.IsNullOrEmpty(criteria.Sort))
            {
                var isDescending = "desc".Equals(criteria.SortOrder, StringComparison.OrdinalIgnoreCase);

                SearchSort sortObject = null;

                switch (criteria.Sort.ToLowerInvariant())
                {
                    case "price":
                        if (serviceCriteria.Pricelists != null)
                        {
                            sortObject = new SearchSort(
                                serviceCriteria.Pricelists.Select(
                                    priceList =>
                                        new SearchSortField(String.Format("price_{0}_{1}", serviceCriteria.Currency.ToLower(), priceList.ToLower()))
                                        {
                                            IgnoredUnmapped = true,
                                            IsDescending = isDescending,
                                            DataType = SearchSortField.DOUBLE
                                        })
                                    .ToArray());
                        }
                        break;
                    case "position":
                        sortObject =
                            new SearchSort(
                                new SearchSortField(string.Concat("sort", catalog, categoryId).ToLower())
                                {
                                    IgnoredUnmapped = true,
                                    IsDescending = isDescending
                                });
                        break;
                    case "name":
                        sortObject = new SearchSort("name", isDescending);
                        break;
                    case "rating":
                        sortObject = new SearchSort(serviceCriteria.ReviewsAverageField, isDescending);
                        break;
                    case "reviews":
                        sortObject = new SearchSort(serviceCriteria.ReviewsTotalField, isDescending);
                        break;
                    default:
                        sortObject = CatalogIndexedSearchCriteria.DefaultSortOrder;
                        break;
                }

                serviceCriteria.Sort = sortObject;
            }

            #endregion

            var responseGroup = ItemResponseGroup.ItemInfo | ItemResponseGroup.ItemAssets | ItemResponseGroup.Seo;

            if ((criteria.ResponseGroup & SearchResponseGroup.WithProperties) == SearchResponseGroup.WithProperties)
            {
                responseGroup |= ItemResponseGroup.ItemProperties;
            }

            if ((criteria.ResponseGroup & SearchResponseGroup.WithVariations) == SearchResponseGroup.WithVariations)
            {
                responseGroup |= ItemResponseGroup.Variations;
            }

            //Load ALL products 
            var searchResults = _browseService.SearchItems(serviceCriteria, responseGroup);

            // populate inventory
            //if ((request.ResponseGroup & ItemResponseGroup.ItemProperties) == ItemResponseGroup.ItemProperties)
            if ((criteria.ResponseGroup & SearchResponseGroup.WithProperties) == SearchResponseGroup.WithProperties)
            {
                PopulateInventory(store.FulfillmentCenter, searchResults.Products);
            }

            return searchResults;
        }
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType != typeof(CatalogIndexedSearchCriteria))
            {
                return false;
            }

            var key = actionContext.Request.RequestUri.Query;
            var qs = HttpUtility.ParseQueryString(key);
            var qsDict = this.NvToDict(qs);

            // parse facets
            var facets =
                qsDict.Where(k => FacetRegex.IsMatch(k.Key))
                    .Select(k => k.WithKey(FacetRegex.Replace(k.Key, "")))
                    .ToDictionary(x => x.Key, y => y.Value.Split(','));

            // parse facets
            var terms =
                qsDict.Where(k => TermRegex.IsMatch(k.Key))
                    .Select(k => k.WithKey(TermRegex.Replace(k.Key, "")))
                    .ToDictionary(x => x.Key, y => y.Value.Split(','));

            var result = new CatalogIndexedSearchCriteria
                         {
                             SearchPhrase = qs["q"].EmptyToNull(),
                             RecordsToRetrieve = qs["take"].TryParse(20),
                             StartingRecord = qs["skip"].TryParse(0),
                         };

            // apply filters if one specified
            if (terms.Count > 0)
            {
                foreach (var term in terms)
                {
                    var termFilter = new AttributeFilter
                                     {
                                         Key = term.Key.ToLowerInvariant(),
                                         Values =
                                             term.Value.Select(
                                                 x =>
                                             new AttributeFilterValue()
                                             {
                                                 Id = x.ToLowerInvariant(),
                                                 Value = x.ToLowerInvariant()
                                             }).ToArray()
                                     };

                    result.Apply(termFilter);
                }
            }

            //result.ClassTypes.Add("Product");

            var startDateFromStr = qs["startdatefrom"].EmptyToNull();

            if (!string.IsNullOrWhiteSpace(startDateFromStr))
            {
                DateTime startDateFrom;

                if (DateTime.TryParse(startDateFromStr, out startDateFrom))
                {
                    result.StartDateFrom = startDateFrom;
                }
            }

            //TODO load pricelists
            result.Pricelists = null;
            result.Currency = qs["curreny"].EmptyToNull();

            var sortQuery = qs["sort"].EmptyToNull();
            var sort = string.IsNullOrEmpty(sortQuery) ? "name" : sortQuery;
            var sortOrder = qs["sortorder"].EmptyToNull();

            var outline = qs["outline"].EmptyToNull();

            var isDescending = "desc".Equals(sortOrder, StringComparison.OrdinalIgnoreCase);

            var catalogId = actionContext.ActionArguments.ContainsKey("catalog")
                ? actionContext.ActionArguments["catalog"]
                : null;

            string categoryId = null;

            if (!string.IsNullOrWhiteSpace(outline))
            {
                categoryId = outline.Split(new[] { '/' }).Last();
            }

            SearchSort sortObject = null;

            switch (sort.ToLowerInvariant())
            {
                case "price":
                    if (result.Pricelists != null)
                    {
                        sortObject = new SearchSort(
                            result.Pricelists.Select(
                                priceList =>
                                    new SearchSortField(
                                        String.Format(
                                            "price_{0}_{1}",
                                            result.Currency.ToLower(),
                                            priceList.ToLower()))
                                    {
                                        IgnoredUnmapped = true,
                                        IsDescending = isDescending,
                                        DataType = SearchSortField.DOUBLE
                                    })
                                .ToArray());
                    }
                    break;
                case "position":
                    sortObject =
                        new SearchSort(
                            new SearchSortField(string.Format("sort{0}{1}", catalogId, categoryId).ToLower())
                            {
                                IgnoredUnmapped = true,
                                IsDescending = isDescending
                            });
                    break;
                case "name":
                    sortObject = new SearchSort("name", isDescending);
                    break;
                case "rating":
                    sortObject = new SearchSort(result.ReviewsAverageField, isDescending);
                    break;
                case "reviews":
                    sortObject = new SearchSort(result.ReviewsTotalField, isDescending);
                    break;
                default:
                    sortObject = CatalogIndexedSearchCriteria.DefaultSortOrder;
                    break;
            }

            result.Sort = sortObject;

            //Use fuzzy search to allow spelling error tolerance
            result.IsFuzzySearch = true;

            bindingContext.Model = result;
            return true;
        }
 protected void AddQueryString(BoolQuery<ESDocument> query, CatalogIndexedSearchCriteria filter, params string[] fields)
 {
     var searchPhrase = filter.SearchPhrase;
     if (filter.IsFuzzySearch)
     {
         query.Must(
             q =>
             q.MultiMatch(
                 x =>
                 x.Fields(fields).Operator(Operator.AND).Fuzziness(filter.FuzzyMinSimilarity).Query(searchPhrase)));
     }
     else
     {
         query.Must(
             q =>
             q.MultiMatch(
                 x =>
                 x.Fields(fields).Operator(Operator.AND).Query(searchPhrase)));
     }
 }
        public void Can_get_item_facets_lucene()
        {
            var scope = "default";
            var queryBuilder = new LuceneSearchQueryBuilder();
            var conn = new SearchConnection(_LuceneStorageDir, scope);
            var provider = new LuceneSearchProvider(queryBuilder, conn);
            Debug.WriteLine("Lucene connection: {0}", conn.ToString());

            if (Directory.Exists(_LuceneStorageDir))
            {
                Directory.Delete(_LuceneStorageDir, true);
            }

            SearchHelper.CreateSampleIndex(provider, scope);

            var criteria = new CatalogIndexedSearchCriteria
            {
                SearchPhrase = "",
                IsFuzzySearch = true,
                Catalog = "goods",
                RecordsToRetrieve = 10,
                StartingRecord = 0,
                Currency = "USD",
                Pricelists = new[] { "default" }
            };

            var filter = new AttributeFilter { Key = "Color" };
            filter.Values = new[]
                                {
                                    new AttributeFilterValue { Id = "red", Value = "red" },
                                    new AttributeFilterValue { Id = "blue", Value = "blue" },
                                    new AttributeFilterValue { Id = "black", Value = "black" }
                                };

            var rangefilter = new RangeFilter { Key = "size" };
            rangefilter.Values = new[]
                                     {
                                         new RangeFilterValue { Id = "0_to_5", Lower = "0", Upper = "5" },
                                         new RangeFilterValue { Id = "5_to_10", Lower = "5", Upper = "10" }
                                     };

            var priceRangefilter = new PriceRangeFilter { Currency = "usd" };
            priceRangefilter.Values = new[]
                                          {
                                              new RangeFilterValue { Id = "0_to_100", Lower = "0", Upper = "100" },
                                              new RangeFilterValue { Id = "100_to_700", Lower = "100", Upper = "700" }
                                          };

            criteria.Add(filter);
            criteria.Add(rangefilter);
            criteria.Add(priceRangefilter);

            var results = provider.Search(scope, criteria);

            Assert.True(results.DocCount == 4, String.Format("Returns {0} instead of 4", results.DocCount));

            var redCount = GetFacetCount(results, "Color", "red");
            Assert.True(redCount == 2, String.Format("Returns {0} facets of red instead of 2", redCount));

            var priceCount = GetFacetCount(results, "Price", "0_to_100");
            Assert.True(priceCount == 2, String.Format("Returns {0} facets of 0_to_100 prices instead of 2", priceCount));

            var priceCount2 = GetFacetCount(results, "Price", "100_to_700");
            Assert.True(priceCount2 == 2, String.Format("Returns {0} facets of 100_to_700 prices instead of 2", priceCount2));

            var sizeCount = GetFacetCount(results, "size", "0_to_5");
            Assert.True(sizeCount == 2, String.Format("Returns {0} facets of 0_to_5 size instead of 2", sizeCount));

            var sizeCount2 = GetFacetCount(results, "size", "5_to_10");
            Assert.True(sizeCount2 == 1, String.Format("Returns {0} facets of 5_to_10 size instead of 1", sizeCount2)); // only 1 result because upper bound is not included

            var outlineCount = results.Documents[0].Documents[0]["__outline"].Values.Count();
            Assert.True(outlineCount == 2, String.Format("Returns {0} outlines instead of 2", outlineCount));

            Directory.Delete(_LuceneStorageDir, true);
        }
        public void Can_get_item_multiple_filters_lucene()
        {
            var scope = "default";
            var queryBuilder = new LuceneSearchQueryBuilder();
            var conn = new SearchConnection(_LuceneStorageDir, scope);
            var provider = new LuceneSearchProvider(queryBuilder, conn);
            Debug.WriteLine("Lucene connection: {0}", conn.ToString());

            if (Directory.Exists(_LuceneStorageDir))
            {
                Directory.Delete(_LuceneStorageDir, true);
            }

            SearchHelper.CreateSampleIndex(provider, scope);

            var criteria = new CatalogIndexedSearchCriteria
            {
                SearchPhrase = "",
                IsFuzzySearch = true,
                Catalog = "goods",
                RecordsToRetrieve = 10,
                StartingRecord = 0,
                Currency = "USD",
                Pricelists = new[] { "default" }
            };

            var colorFilter = new AttributeFilter { Key = "Color" };
            colorFilter.Values = new[]
                                {
                                    new AttributeFilterValue { Id = "red", Value = "red" },
                                    new AttributeFilterValue { Id = "blue", Value = "blue" },
                                    new AttributeFilterValue { Id = "black", Value = "black" }
                                };

            var filter = new AttributeFilter { Key = "Color" };
            filter.Values = new[]
                                {
                                    new AttributeFilterValue { Id = "black", Value = "black" }
                                };

            var rangefilter = new RangeFilter { Key = "size" };
            rangefilter.Values = new[]
                                     {
                                         new RangeFilterValue { Id = "0_to_5", Lower = "0", Upper = "5" },
                                         new RangeFilterValue { Id = "5_to_10", Lower = "5", Upper = "11" }
                                     };

            var priceRangefilter = new PriceRangeFilter { Currency = "usd" };
            priceRangefilter.Values = new[]
                                          {
                                              new RangeFilterValue { Id = "100_to_700", Lower = "100", Upper = "700" }
                                          };

            criteria.Add(colorFilter);
            criteria.Add(priceRangefilter);

            // add applied filters
            criteria.Apply(filter);
            //criteria.Apply(rangefilter);
            //criteria.Apply(priceRangefilter);

            var results = provider.Search(scope, criteria);

            var blackCount = GetFacetCount(results, "Color", "black");
            Assert.True(blackCount == 1, String.Format("Returns {0} facets of black instead of 2", blackCount));

            //Assert.True(results.DocCount == 1, String.Format("Returns {0} instead of 1", results.DocCount));

            Directory.Delete(_LuceneStorageDir, true);
        }