4
votes

Im trying to run aggregation query in c# (using nest 5) but I dont know how many aggregations I get as input and what are the aggregation type.

For example one query is: {"aggs":{"type_count":{"terms":{"field":"type"}}}}

And other query will be: {"aggs":{"type_count":{"terms":{"field":"type"}},"salary_count": {"field":"salary"}}}

And other query may not contain aggregation at all.

How can I write this code dynamic in c#?

This is what I tried (I have case for selected aggregation types. The problem is that this code support only one aggregation.

SearchDescriptor<object> SearchAgg = new SearchDescriptor<object>();
for (i=0;i < aggList.length;i++)
{
    SearchAgg.Aggregations(a => a.terms (aggList[i]), t=> t.Field(aggList[i]));
}

Edit:

I successed to add multiple aggregations using this code:

AggregationContainerDescriptor<SearchRequest> agg = new
AggregationContainerDescriptor<SearchRequest>();

agg.Terms("bucket", tm=> tm.Field("field"));
agg &= new AggregationContainerDescriptor<SearchRequest>().Terms("bucket2", tm=> tm.Field("field2"));

Thanks

1
Take a look at the documentation on writing aggregations: elastic.co/guide/en/elasticsearch/client/net-api/current/…Russ Cam

1 Answers

6
votes

Generally speaking, method calls using the Fluent lambda expression syntax within NEST perform assignment as opposed to being additive, meaning that successive calls of the same method will overwrite what is assigned. In your example here

SearchDescriptor<object> SearchAgg = new SearchDescriptor<object>();
for (i=0;i < aggList.length;i++)
{
    SearchAgg.Aggregations(a => a.terms (aggList[i]), t=> t.Field(aggList[i]));
}

only the last call to SearchAgg.Aggregations(...) will assign.

The writing aggregations documentation has examples of issuing multiple aggregations. Given the following POCOs

public class Project
{
    public string Name { get; set; }
    public string Description { get; set; }
    public DateTime StartedOn { get; set; }
    public DateTime LastActivity { get; set; }
    public IList<string> Tags { get; set; }
    public IList<string> Branches { get; set; }
    public IList<CommitActivity> Commits { get; set; }
}

public class CommitActivity
{
    public string Id { get; set; }
    public string Message { get; set; }
    public long SizeInBytes { get; set; }
}

where CommitActivity is mapped as a nested type, issuing two terms aggregation with a nested aggregation on commits to aggregate stats about commits for each project

Using fluent lambda expression syntax

var searchResponse = client.Search<Project>(s => s
    .Aggregations(aggs => aggs
        .Terms("project_tags", t => t.Field(p => p.Tags))
        .Terms("project_branches", t => t.Field(p => p.Branches))
        .Nested("commits", n => n
            .Path(p => p.Commits)
            .Aggregations(aa => aa
                .Stats("commit_size_stats", m => m.Field(p => p.Commits.First().SizeInBytes))
            )
        )
    )
);

Object Initializer syntax

var searchRequest = new SearchRequest<Project>
{
    Aggregations = new AggregationDictionary
    {
        { "project_tags", new TermsAggregation("project_tags") { Field = Nest.Infer.Field<Project>(p => p.Tags) } },
        { "project_branches", new TermsAggregation("project_branches") { Field = Nest.Infer.Field<Project>(p => p.Branches) } },
        { "commits", new NestedAggregation("commits") 
            {
                Path = Nest.Infer.Field<Project>(p => p.Commits),
                Aggregations = new AggregationDictionary
                {
                    { "commit_size_stats", new StatsAggregation("commit_size_stats", Nest.Infer.Field<Project>(p => p.Commits.First().SizeInBytes)) },
                }
            }
        }
    }
};

var searchResponse = client.Search<Project>(searchRequest);

Since ultimately aggregations on a search request is just a dictionary of aggregation name and the type of aggregation, using this syntax can grow big very quickly. for this reason, NEST overloads the logical && operator and implements implicit conversions to allow combining aggregations in a more terse fashion

Terse Object Initializer syntax

var searchRequest = new SearchRequest<Project>
{
    Aggregations = 
        new TermsAggregation("project_tags") { Field = Nest.Infer.Field<Project>(p => p.Tags) } &&
        new TermsAggregation("project_branches") { Field = Nest.Infer.Field<Project>(p => p.Branches) } &&
        new NestedAggregation("commits") 
        {
            Path = Nest.Infer.Field<Project>(p => p.Commits),
            Aggregations = 
                new StatsAggregation("commit_size_stats", Nest.Infer.Field<Project>(p => p.Commits.First().SizeInBytes))
        }
};

var searchResponse = client.Search<Project>(searchRequest);