Example #1
0
        /// <summary>
        /// Helper function to extract the aggs for a simple (e.g. non-toolType) aggregation
        /// </summary>
        /// <returns>The simple aggs.</returns>
        /// <param name="facetConfig">Configuration for the field being aggregating</param>
        /// <param name="res">Res.</param>
        private IEnumerable <KeyLabelAggResult> ExtractAggResults(R4RAPIOptions.FacetConfig facetConfig, ISearchResponse <Resource> res)
        {
            var currBucket = res.Aggs.Nested($"{facetConfig.FilterName}_agg");

            //We need to go one level deeper if this has a dependent filter
            if (!String.IsNullOrWhiteSpace(facetConfig.RequiresFilter))
            {
                currBucket = currBucket.Filter($"{facetConfig.FilterName}_filter");
            }

            var keys = currBucket.Terms($"{facetConfig.FilterName}_key");

            foreach (var keyBucket in keys.Buckets)
            {
                long   count = keyBucket.DocCount ?? 0;
                string key   = keyBucket.Key;

                var label        = "";
                var labelBuckets = keyBucket.Terms($"{facetConfig.FilterName}_label").Buckets;
                if (labelBuckets.Count > 0)
                {
                    label = labelBuckets.First().Key;
                }

                yield return(new KeyLabelAggResult()
                {
                    Key = key,
                    Label = label,
                    Count = count
                });
            }
        }
        /// <summary>
        /// Transforms the string name and returned aggregate results into a Facet
        /// </summary>
        /// <returns>An facet</returns>
        /// <param name="facetToFetch">The name of the facet to fetch</param>
        /// <param name="resourceQuery">The resource query (used for determining currently-set filters)</param>
        /// <param name="aggResults">The list of aggregate results to transform to facet items</param>
        private Facet TransformFacet(string facetToFetch, ResourceQuery resourceQuery, KeyLabelAggResult[] aggResults)
        {
            R4RAPIOptions.FacetConfig config = _apiOptions.AvailableFacets[facetToFetch];

            IEnumerable <string> filters = new string[] { };

            if (resourceQuery.Filters.ContainsKey(config.FilterName))
            {
                filters = resourceQuery.Filters[config.FilterName];
            }

            Facet facet = new Facet
            {
                Param = config.FilterName,
                Title = config.Label,
                Items = aggResults.Select(fi =>
                                          new FacetItem
                {
                    Key      = fi.Key,
                    Label    = fi.Label,
                    Count    = Convert.ToInt32(fi.Count),
                    Selected = filters.Contains(fi.Key)
                }).ToArray()
            };

            if (config.FacetType == R4RAPIOptions.FacetTypes.Single && IsFilterSet(config.FilterName, resourceQuery))
            {
                facet.Items = facet.Items.Where(i => i.Selected).ToArray();
            }

            return(facet);
        }
        /// <summary>
        /// Determines whether a facet should be fetched
        /// This returns false if a facet requires a different filter to be set, and that other filter isn't set
        /// </summary>
        /// <returns>A boolean, indicating whether to fetch the given facet</returns>
        /// <param name="facetName">A list of facet names to include in our search</param>
        /// <param name="resourceQuery">The resource query (used for determining currently-set filters)</param>
        private bool ShouldFetchFacet(string facetName, ResourceQuery resourceQuery)
        {
            R4RAPIOptions.FacetConfig config = _apiOptions.AvailableFacets[facetName];

            var filters = (!string.IsNullOrWhiteSpace(config.RequiresFilter) && resourceQuery.Filters.ContainsKey(config.RequiresFilter)) ? resourceQuery.Filters[config.RequiresFilter] : new string[] { };

            if (!string.IsNullOrWhiteSpace(config.RequiresFilter) && filters.Length == 0)
            {
                return(false);
            }
            else
            {
                return(true);
            }
        }
Example #4
0
        /// <summary>
        /// Gets the simple aggregation "query"
        /// </summary>
        /// <returns>An AggregationDictionary containing the "query"</returns>
        /// <param name="facetConfig">Configuration for the field to aggregate</param>
        /// <param name="query"></param>
        private AggregationDictionary GetAggQuery(R4RAPIOptions.FacetConfig facetConfig, ResourceQuery query)
        {
            // If we *really* need the parentKey for this facet, then we must add it to the aggregation.
            // however, we may not really need it.
            var keyLabelAggregate = new TermsAggregation($"{facetConfig.FilterName}_key")
            {
                Field = new Field($"{facetConfig.FilterName}.key"), //Set the field to rollup
                Size  = 999,                                        //Use a large number to indicate unlimted (def is 10)
                                                                    // Now, we need to get the labels for the keys and thus
                                                                    // we need to add a sub aggregate for this term.
                                                                    // Normally you would do this for something like city/state rollups
                Aggregations = new TermsAggregation($"{facetConfig.FilterName}_label")
                {
                    Field = new Field($"{facetConfig.FilterName}.label")
                }
            };

            AggregationDictionary aggBody = keyLabelAggregate;

            // This facet requires a parent and thus needs a filter aggregate
            // to wrap the keyLabelAggregate
            if (!String.IsNullOrWhiteSpace(facetConfig.RequiresFilter))
            {
                aggBody = new FilterAggregation($"{facetConfig.FilterName}_filter")
                {
                    Filter       = this.GetQueryForFilterField($"{facetConfig.FilterName}.parentKey", query.Filters[facetConfig.RequiresFilter]),
                    Aggregations = keyLabelAggregate
                };
            }

            //Start with a nested aggregation
            var agg = new NestedAggregation($"{facetConfig.FilterName}_agg")
            {
                Path = new Field(facetConfig.FilterName), //Set the path of the nested agg

                // Add the sub aggregates (bucket keys)
                Aggregations = aggBody
            };

            return(agg);
        }
Example #5
0
        /// <summary>
        /// Asynchronously gets the key label aggregation for a field
        /// </summary>
        /// <param name="field">The field to aggregate</param>
        /// <param name="query">The query for the results</param>
        /// <returns>The aggregation items</returns>
        public async Task <KeyLabelAggResult[]> GetKeyLabelAggregationAsync(string field, ResourceQuery query)
        {
            if (string.IsNullOrWhiteSpace(field))
            {
                throw new ArgumentNullException(nameof(field));
            }

            if (query == null)
            {
                throw new ArgumentNullException(nameof(query));
            }

            if (!this._apiOptions.AvailableFacets.ContainsKey(field))
            {
                throw new ArgumentException($"Field, {field}, does not have any configuration");
            }

            //Get config
            R4RAPIOptions.FacetConfig facetConfig = this._apiOptions.AvailableFacets[field];

            //Make sure we have a valid config.
            if (facetConfig == null)
            {
                throw new ArgumentNullException($"Facet configuration could not be found for {field}");
            }

            if (
                !string.IsNullOrWhiteSpace(facetConfig.RequiresFilter) &&
                (!query.Filters.ContainsKey(facetConfig.RequiresFilter) || query.Filters[facetConfig.RequiresFilter].Length == 0)
                )
            {
                throw new ArgumentException($"Facet, {facetConfig.FilterName}, requires filter, {facetConfig.RequiresFilter}");
            }

            Indices       index = Indices.Index(new string[] { this._apiOptions.AliasName });
            Types         types = Types.Type(new string[] { "resource" });
            SearchRequest req   = new SearchRequest(index, types)
            {
                Size = 0, //req.Size = 0; //Set the size to 0 in order to return no Resources

                // Let's check if the field has a period if it does, then we need
                // to handle it differently because it is either toolType.type or
                // toolType.subtype.
                Aggregations = GetAggQuery(facetConfig, query)
            };

            var searchQuery = GetSearchQueryForFacet(facetConfig.FilterName, query);

            if (searchQuery != null)
            {
                req.Query = searchQuery;
            }

            try
            {
                //We must(?) pass the C# type to map the results to
                //even though our queries should not return anything.
                var res = await this._elasticClient.SearchAsync <Resource>(req);

                return(ExtractAggResults(facetConfig, res).ToArray());
            }
            catch (Exception ex)
            {
                this._logger.LogError($"Could not fetch aggregates for field: {field}");
                throw new Exception($"Could not fetch aggregates for field: {field}", ex);
            }
        }