Dynamic aggregation

This feature is an another way to do aggregations and in contrast to map/reduce indexes, it allows to create much more complex queries. It gives you more options for reporting applications, dynamic selection and complex aggregation with additional filtering.

When working with a map/reduce index we are able to do only limited amount of queries. Let's imagine a sample SQL query:

select sum(Total) from Orders where Total > 500 group by Product

In order give you the ability to query like this, we introduced the dynamic aggregation feature. Thanks this you can build the following query:

var result = session.Query<Order>("Orders/All")
                    .Where(x => x.Total > 500)
                    .AggregateBy(x => x.Product)
						.SumOn(x => x.Total)
                    .ToList();

The Orders/All index used in the query is a simple map-only index. The only difference is that you have to specify sort options for numeric fields used in the query. This is needed for recognizing by RavenDB types of numeric fields when such a query will come in.

store.DatabaseCommands.PutIndex("Orders/All", new IndexDefinitionBuilder<Order>()
{
	Map = orders => from order in orders
					select new
					{
						order.Total,
						order.Product,
						order.Concurrency
					},
	SortOptions = { { x => x.Product, SortOptions.Double } }
});

Note

Results of a dynamic aggregation query are calculated on the fly, while results of map/reduce index are precomputed. Most cases is fast enough but note that you can hit a lot of data. This feature should be used only for complex aggregate queries that cannot be done by using standard map/reduce.

Under the covers this is a faceted search with an extended support for doing aggregations. For example you can aggregate ranges:

result = session.Query<Order>("Orders/All")
                .AggregateBy(x => x.Product)
                .AddRanges(x => x.Total < 100,
                           x => x.Total >= 100 && x.Total < 500,
                           x => x.Total >= 500 && x.Total < 1500,
                           x => x.Total >= 1500)
                .SumOn(x => x.Total)
                .ToList();

It also supports an aggregation on multiple levels:

result = session.Query<Order>("Orders/All")
                .AggregateBy(x => x.Product)
					.SumOn(x => x.Total)
					.CountOn(x => x.Total)
                .AndAggregateOn(x => x.Concurrency)
					.MinOn(x => x.Total)
                .ToList();

Another example is an aggregation on different fields based on same facet:

result = session.Query<Order>("Orders/All")
                .AggregateBy(x => x.Product)
					.MaxOn(x => x.Total)
					.MinOn(x => x.Concurrency)
				.ToList();