RavenDB Survival Tip #2: Simulating select count(*)

Categories: RavenDB

Tags: RavenDB, SQL

A lot of the time a user will ask for a count of some business entity on a report so they can tell how many of something is happening.  With SQL, this is a very natural process with aggregate operations:

select count(*) from Users where IsAdmin = true

 

But with RavenDB, I found it difficult to wrap my mind around how you would do this with an Index.  Aggregate operations like this are implemented with map/reduce indexes.  Since the reduce step of the index needs to have the same output shape as the map, we can’t simply return a single numeric value as is the case with SELECT COUNT(*) in SQL.  We need to perform a LINQ group-by.  But in this case, I don’t really want to group by anything in a document, I just want a count. 

After a little thinking and digging around, I found a solution.  Maybe this is obvious to most, but I found that if we simply group by a constant, we can get a single numeric value for that constant the represents the total count.

Here’s a sample Index definition that demonstrates this concept.  In the Index, I am trying to find all User documents that qualify as admins (IsAdmin = true).

public class Users_AdminCounts : AbstractIndexCreationTask<User, Users_AdminCounts.ReduceResult>
{
    public class ReduceResult
    {
        public string Name { get; set; }
        public int Count { get; set; }
    }

    public Users_AdminCounts()
    {
        // the Name parameter to the reduce function is dummy
        // to get it to aggregate to one single value
        Map = users =>
            from user in users
            where user.IsAdmin = true
            select new { Name = "Total", Count = 1 };

        Reduce = results =>
            from result in results
            group result by result.Name
            into g
            select new { Name = g.Key, Count = g.Sum(x => x.Count) };
    }
}

 

When you run the query and get the result (ReduceResult), you will get just one single result and the Count property will contain the count aggregate you are looking for.

No Comments