public FilterResult Build() { var productCatalogData = _routeRequestInfoAccessor.RouteRequestInfo?.Data as ProductPageData; var pageModel = _requestModelAccessor.RequestModel.CurrentPageModel; if (productCatalogData == null && !pageModel.IsBrandPageType() && !pageModel.IsProductListPageType() && !pageModel.IsSearchResultPageType()) { return(null); } var filterNavigation = _requestModelAccessor.RequestModel.WebsiteModel.GetNavigationType() == NavigationType.Filter; var searchQuery = _requestModelAccessor.RequestModel.SearchQuery; if ((searchQuery.Type != SearchType.Products && !filterNavigation) || (pageModel.IsSearchResultPageType() && string.IsNullOrEmpty(searchQuery.Text))) { return(null); } var propertyNames = GetPropertyNames(searchQuery); return(new FilterResult { Items = _filterAggregator.GetFilter(searchQuery, propertyNames).ToList() }); }
private List <ContentLinkModel> GetFilters(CategoryModel categoryModel, List <string> selectedFilters) { var result = new List <ContentLinkModel>(); var allFilterFields = _filterService.GetProductFilteringFields(); var fields = selectedFilters.Where(x => allFilterFields.Contains(x)).ToList(); var categoryShowRecursively = _requestModelAccessor.RequestModel.WebsiteModel.GetNavigationType() == NavigationType.Filter; var searchQuery = new SearchQuery { CategorySystemId = categoryModel.SystemId, CategoryShowRecursively = categoryShowRecursively }; foreach (var filterGroup in _filterAggregator.GetFilter(searchQuery, fields)) { result.Add(filterGroup.MapTo <ContentLinkModel>()); } return(result); }
public override IEnumerable <GroupFilter> GetFilter(SearchQuery searchQuery, IEnumerable <string> fieldNames) { var fieldNamesList = fieldNames as IList <string> ?? fieldNames.ToList(); if (!_searchClientService.IsConfigured) { foreach (var item in _parent.GetFilter(searchQuery, fieldNames)) { yield return(item); } } else { if (fieldNamesList.Count > 0 && !fieldNamesList.All(fieldName => fieldName.Equals(FilteringConstants.FilterNews, StringComparison.OrdinalIgnoreCase))) { var noFilterFieldNames = new HashSet <string>(new[] { FilteringConstants.FilterPrice, FilteringConstants.FilterNews, FilteringConstants.FilterProductCategories }, StringComparer.OrdinalIgnoreCase); var result = _searchClientService.Search <ProductDocument>(CultureInfo.CurrentCulture, descriptor => descriptor .Size(0) .QueryWithPermission(queryContainerDescriptor => _searchQueryBuilder.BuildQuery(queryContainerDescriptor, searchQuery, tags: null, addPriceFilterTags: false, addNewsFilterTags: false, addCategoryFilterTags: false, addDefaultQuery: true)) .Aggregations(rootAgg => { var aggs = new List <AggregationContainerDescriptor <ProductDocument> >(); var aggregationTagNames = fieldNames .Where(fieldName => !noFilterFieldNames.Contains(fieldName)); aggs.AddRange(BuildFieldAggregations(rootAgg, aggregationTagNames)); aggs.Add(BuildFieldAggregation(rootAgg, aggregationTagNames)); if (fieldNamesList.Any(fieldName => fieldName.Equals(FilteringConstants.FilterPrice, StringComparison.OrdinalIgnoreCase))) { aggs.Add(BuildPriceAggregation(rootAgg)); } if (fieldNamesList.Any(fieldName => fieldName.Equals(FilteringConstants.FilterProductCategories, StringComparison.OrdinalIgnoreCase))) { aggs.Add(BuildCategoryAggregation(rootAgg)); } return(aggs.Aggregate((a, b) => a & b)); })); foreach (var fieldName in fieldNames) { if (fieldName.Equals(FilteringConstants.FilterPrice, StringComparison.OrdinalIgnoreCase)) { var filterGroup = CollectPriceFacet(); if (filterGroup is object) { yield return(filterGroup); } } else if (fieldName.Equals(FilteringConstants.FilterNews, StringComparison.OrdinalIgnoreCase)) { var filterGroup = GetNewsTag(searchQuery); if (filterGroup is object) { yield return(filterGroup); } } else if (fieldName.Equals(FilteringConstants.FilterProductCategories, StringComparison.OrdinalIgnoreCase)) { var filterGroup = CollectCategoryFacet(); if (filterGroup is object) { yield return(filterGroup); } } else { var tag = CollectFieldFacet(fieldName); if (tag is object) { yield return(tag); } } } AggregationContainerDescriptor <ProductDocument> BuildCategoryAggregation(AggregationContainerDescriptor <ProductDocument> selector) { return(selector .Nested("$Categories", filterContainer => filterContainer .Path(x => x.MainCategories) .Aggregations(a => a .Filter("filter", filterSelector => filterSelector .Filter(ff => ff .Bool(bq => bq .Must(m => { var qc = m .Term(t => t .Field(x => x.MainCategories[0].AssortmentSystemId) .Value(_assortmentSystemId.Value) ); if (searchQuery.ContainsFilter() && (!searchQuery.ContainsCategoryFilter() || searchQuery.ContainsMultipleFilters())) { qc &= _searchQueryBuilder.BuildQuery( m, searchQuery, tags: searchQuery.Tags, addPriceFilterTags: true, addNewsFilterTags: true, addCategoryFilterTags: false, addDefaultQuery: false); } return qc; }) ) ) .Aggregations(termAgg => termAgg .Terms("tags", termSelector => termSelector .Field(x => x.MainCategories[0].AssortmentSystemId) .Aggregations(subAggregation => subAggregation .Terms("tag", valueSelector => valueSelector .Field(x => x.MainCategories[0].CategorySystemId) ) ) ) ) ) ) )); } IEnumerable <AggregationContainerDescriptor <ProductDocument> > BuildFieldAggregations(AggregationContainerDescriptor <ProductDocument> selector, IEnumerable <string> fieldNames) { return(fieldNames .Select(fieldName => { if (searchQuery.ContainsFilter(exceptTag: fieldName)) { return selector .Filter(fieldName, filterContainer => filterContainer .Filter(filterSelector => filterSelector .Bool(bq => bq .Must(m => _searchQueryBuilder.BuildQuery( m, searchQuery, tags: FilterFields(fieldName), addPriceFilterTags: true, addNewsFilterTags: true, addCategoryFilterTags: true, addDefaultQuery: false) ) ) ) .Aggregations(x => BuildFilterAggregation(x, fieldName)) ); } return BuildFilterAggregation(selector, fieldName); })); AggregationContainerDescriptor <ProductDocument> BuildFilterAggregation(AggregationContainerDescriptor <ProductDocument> container, string fieldName) { return(container .Nested(fieldName, nestedPerField => nestedPerField .Path(x => x.Tags) .Aggregations(fieldAggregation => fieldAggregation .Filter("filter", fieldFilter => fieldFilter .Filter(filter => filter .Term(filterTerm => filterTerm .Field(field => field.Tags[0].Key) .Value(fieldName) ) ) .Aggregations(tags => tags .Terms("tags", termSelector => termSelector .Field(field => field.Tags[0].Key) .Aggregations(subAggregation => subAggregation .Terms("tag", tag => tag .Field(x => x.Tags[0].Value) ) ) ) ) ) ) )); } IDictionary <string, ISet <string> > FilterFields(string fieldName) { return(searchQuery .Tags .Where(x => !x.Key.Equals(fieldName, StringComparison.OrdinalIgnoreCase)) .ToDictionary(x => x.Key, x => x.Value)); } } AggregationContainerDescriptor <ProductDocument> BuildFieldAggregation(AggregationContainerDescriptor <ProductDocument> selector, IEnumerable <string> fieldNames) { return(selector .Nested("$all-tags", filterContainer => filterContainer .Path(x => x.Tags) .Aggregations(a => a .Filter("filter", filterSelector => filterSelector .Filter(ff => ff .Bool(bq => bq .Must(m => m .Terms(t => t .Field(x => x.Tags[0].Key) .Terms(fieldNames) ) ) ) ) .Aggregations(termAgg => termAgg .Terms("tags", termSelector => termSelector .Field(x => x.Tags[0].Key) .Aggregations(subAggregation => subAggregation .Terms("tag", valueSelector => valueSelector .Field(x => x.Tags[0].Value) ) ) ) ) ) ) )); } AggregationContainerDescriptor <ProductDocument> BuildPriceAggregation(AggregationContainerDescriptor <ProductDocument> selector) { if (searchQuery.ContainsFilter(includePriceFilter: false)) { return(selector .Filter("$Prices", filterContainer => filterContainer .Filter(filterSelector => filterSelector .Bool(bq => bq .Must(m => _searchQueryBuilder.BuildQuery( m, searchQuery, tags: searchQuery.Tags, addPriceFilterTags: false, addNewsFilterTags: true, addCategoryFilterTags: true, addDefaultQuery: false) ) ) ) .Aggregations(x => BuildPriceAggregationItem(x)) )); } else { return(BuildPriceAggregationItem(selector)); }; AggregationContainerDescriptor <ProductDocument> BuildPriceAggregationItem(AggregationContainerDescriptor <ProductDocument> selector) { return(selector .Terms("$Prices", prices => prices.Script(script => script .Source("double r; for (item in params._source.prices) { if (params.id.contains(item.systemId) && item.countrySystemId == params.country && item.isCampaignPrice == false) { r = r == 0 ? item.price : Math.min(r, item.price)}} return r") .Params(new Dictionary <string, object> { { "id", _priceContainer.Value.PriceLists.ToArray() }, { "country", _countrySystemId.Value }, })))); } } GroupFilter CollectCategoryFacet() { var categoryBucket = result.Aggregations .Global("$Categories") .Filter("filter")? .Terms("tags")? .Buckets .FirstOrDefault()? .Terms("tag")? .Buckets .Select(x => new { CanConvert = Guid.TryParse(x.Key, out var id), Key = id, x.DocCount }) .Where(x => x.CanConvert) .ToDictionary(x => x.Key, x => unchecked ((int)x.DocCount)); if (categoryBucket == null) { return(null); } return(GetProductCategoryTag(searchQuery, categoryBucket)); } GroupFilter CollectFieldFacet(string fieldName) { var fieldDefinition = _fieldDefinitionService.Get <ProductArea>(fieldName); if (fieldDefinition == null) { return(null); } var allBuckets = result.Aggregations .Global("$all-tags") .Filter("filter")? .Terms("tags")? .Buckets .FirstOrDefault(x => x.Key.Equals(fieldName, StringComparison.OrdinalIgnoreCase))? .Terms("tag")? .Buckets; if (allBuckets == null) { return(null); } var topNode = result.Aggregations.Filter(fieldName); var tagBuckets = (topNode?.Nested(fieldName) ?? topNode)? .Filter("filter")? .Terms("tags")? .Buckets .FirstOrDefault()? .Terms("tag")? .Buckets; var tagValues = new Dictionary <string, int>(); foreach (var item in allBuckets) { var current = tagBuckets?.FirstOrDefault(x => x.Key.Equals(item.Key, StringComparison.OrdinalIgnoreCase)); tagValues.Add(item.Key, unchecked ((int)(current?.DocCount ?? 0))); } return(GetFilterTag(searchQuery, fieldDefinition, tagValues)); } GroupFilter CollectPriceFacet() { var priceBucket = result.Aggregations?.Filter("$Prices")?.Terms("$Prices")?.Buckets ?? result.Aggregations.Terms("$Prices")?.Buckets; if (priceBucket != null) { var priceFacets = priceBucket.ToDictionary(x => decimal.Parse(x.Key), x => unchecked ((int)x.DocCount.GetValueOrDefault())); var keys = priceFacets.Keys.Where(x => x > decimal.Zero).ToArray(); var minPrice = keys.Length > 0 ? (int)Math.Abs(keys.Min()) : 0; var maxPrice = keys.Length > 0 ? (int)Math.Floor(keys.Max()) : 0; var priceHits = GetPriceGroups(priceFacets, minPrice, maxPrice).ToList(); return(GetPriceTag(searchQuery, priceHits, true, _requestModelAccessor.RequestModel.CountryModel.Country.CurrencySystemId)); } return(null); } } }