public override async Task <IEnumerable <GroupFilter> > GetFilterAsync(SearchQuery searchQuery, IEnumerable <string> fieldNames) { var fieldNamesList = fieldNames as IList <string> ?? fieldNames.ToList(); if (!_searchClientService.IsConfigured) { return(await _parent.GetFilterAsync(searchQuery, fieldNames)); } 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 = await _searchClientService.SearchAsync <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)); })); return(CollectGroupFilter()); IEnumerable <GroupFilter> CollectGroupFilter() { 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) .Size(100) ) ) ) ) ) ) )); } 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) .Size(100) ) ) ) ) ) ) )); } 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) .Size(100) ) ) ) ) ) ) )); } 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 }, })) .Size(10000))); } } 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, NumberStyles.Any, CultureInfo.InvariantCulture), 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); } }
public override IEnumerable <GroupFilter> GetFilter(SearchQuery searchQuery, IEnumerable <string> fieldNames) { var filterFullSearch = _productSearchService.Search(searchQuery, null, false, false, false); var filterTagTerms = filterFullSearch.Hits .SelectMany(x => x.Tags.Distinct(TagComparer.Default)) .ToLookup(x => x.Name, StringComparer.OrdinalIgnoreCase) .ToDictionary(x => x.Key, x => x .ToLookup(z => z.Value, StringComparer.OrdinalIgnoreCase) .ToDictionary(z => z.Key, z => z.Count(), StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); foreach (var fieldName in fieldNames) { if (fieldName.Equals(FilteringConstants.FilterPrice, StringComparison.OrdinalIgnoreCase)) { var fullSearchResult = filterFullSearch .Hits .Cast <ProductSearchServiceImpl.SortableSearchHit>() .Select(x => x.Price) .ToLookup(x => x) .ToDictionary(x => x.Key, x => x.Count()); var currentTagSearchResult = searchQuery.ContainsFilter() ? _productSearchService .Search(searchQuery, searchQuery.Tags.ToDictionary(x => x.Key, x => x.Value), false, true, true) .Hits .Cast <ProductSearchServiceImpl.SortableSearchHit>() .Select(x => x.Price) .ToLookup(x => x) .ToDictionary(x => x.Key, x => x.Count()) : fullSearchResult; var keys = fullSearchResult.Keys.Where(x => x > decimal.MinusOne).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(currentTagSearchResult, minPrice, maxPrice).ToList(); var filterGroup = GetPriceTag(searchQuery, priceHits, true, _requestModelAccessor.RequestModel.CountryModel.Country.CurrencySystemId); if (filterGroup != null) { yield return(filterGroup); } } else if (fieldName.Equals(FilteringConstants.FilterNews, StringComparison.OrdinalIgnoreCase)) { var filterGroup = GetNewsTag(searchQuery); if (filterGroup != null) { yield return(filterGroup); } } else if (fieldName.Equals(FilteringConstants.FilterProductCategories, StringComparison.OrdinalIgnoreCase)) { if (filterTagTerms.TryGetValue(TagNames.CategorySystemId, out Dictionary <string, int> tags)) { var filterGroup = GetProductCategoryTag(searchQuery, tags.ToDictionary(x => new Guid(x.Key), x => x.Value)); if (filterGroup != null) { yield return(filterGroup); } } } else { var fieldDefinition = fieldName.GetFieldDefinitionForProducts(); if (fieldDefinition == null) { continue; } var tagName = fieldDefinition.GetTagName(CultureInfo.CurrentCulture); if (filterTagTerms.TryGetValue(tagName, out Dictionary <string, int> tagValues) && tagValues.Count > 0) { var currentTagValues = tagValues; var containsFilter = searchQuery.ContainsFilter(); if (containsFilter) { var currentResult = _productSearchService .Search(searchQuery, searchQuery.Tags.Where(x => !x.Key.Equals(fieldName, StringComparison.OrdinalIgnoreCase)).ToDictionary(x => x.Key, x => x.Value), true, true, true) .Hits .SelectMany(x => x.Tags.Distinct(TagComparer.Default)) .Where(x => x.Name.Equals(tagName, StringComparison.OrdinalIgnoreCase)) .Select(x => x.Value) .ToLookup(x => x, StringComparer.OrdinalIgnoreCase) .ToDictionary(x => x.Key, x => x.Count(), StringComparer.OrdinalIgnoreCase); currentTagValues = tagValues.ToDictionary(x => x.Key, x => currentResult.TryGetValue(x.Key, out var value) ? value : 0); } var filterGroup = GetFilterTag(searchQuery, fieldDefinition, currentTagValues); if (filterGroup != null) { yield return(filterGroup); } } } } }