Counting a field with a specific value in a map/reduce index

42 Views Asked by At

I am new to using map/reduce indexes so my question might be obvious, but I want to count the number of documents where a specific field has a specific value using a map/reduce index.

Let's say that I have documents with a field called activated that can be either true or false. I know I can count all the documents, using the Sum in the reduce function, but how to I make that Sum only happen when activated is true?

Here is an example of such document created each time a user opens a session:

{
"userId": "user1",
"activated": true,
"loginDuration":  123
}

In Studio, I have created the following map function:

from user in docs.Users
select new {
    user.userId,
   Count = 1,
   user.loginDuration,
   AvgLoginDuration = 0
}

and the reduce function:

from r in results
group r by new r.userId into g
let totalLoginDuration = g.Sum(x => x.loginDuration)
let count = g.Sum(x => x.Count)
select new
{
    userId = g.Key.userId,
    Count = count,
    TotalLoginDuration = totalLoginDuration,
    AvgLoginDuration = totalLoginDuration / count
}

I would like to add to the map/reduce index a count of when activated is true, but don't know how to do that. I have tried doing this:

from user in docs.Users
select new {
    user.userId,
   user.activated,
   Count = 1,
   user.loginDuration,
   AvgLoginDuration = 0,
   NumActivated = 0
}

from r in results
group r by new r.userId into g
let totalLoginDuration = g.Sum(x => x.loginDuration)
let count = g.Sum(x => x.Count)
select new
{
    userId = g.Key.userId,
    activated = g.Key.activated,
    Count = count,
    TotalLoginDuration = totalLoginDuration,
    AvgLoginDuration = totalLoginDuration / count,
   NumActivated = g.Sum(x => x.activated == true)
}

but it doesn't save and then I tried this:

from user in docs.Users
select new {
    user.userId,
   Count = 1,
   user.loginDuration,
   AvgLoginDuration = 0,
   NumActivated = user.activated == true ? 1 : 0
}

from r in results
group r by new r.userId into g
let totalLoginDuration = g.Sum(x => x.loginDuration)
let count = g.Sum(x => x.Count)
select new
{
    userId = g.Key.userId,
    Count = count,
    TotalLoginDuration = totalLoginDuration,
    AvgLoginDuration = totalLoginDuration / count,
   NumActivated = g.Sum(x => x.NumActivated == 1)
}

which saves, but then creates an error when the index is run saying it can't convert bool to decimal. How can I make this work?

Thanks a lot in advance, Bertrand.

1

There are 1 best solutions below

1
On

Seems all you need to do is control the 'Count' in the Map part of the index.
Logic such as:

Count = user.activated ? 1 : 0

If activated is 0 the document will Not be counted in the reduce phase.

Here is some example that follows a similar logic:

private class ActiveUsers : AbstractIndexCreationTask<User, ActiveUsersResult>
{
    public ActiveUsers()
    {
        Map = users => from user in users
            select new
            {
                Name = user.Name,
                Count = user.Activated ? 1 : 0
            };

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


    private class ActiveUsersResult
    {
        public string Name { get; set; }
        public int Count { get; set; }
    }