I'm looking for an example of how to implement and use Map-Reduce within the RavenDB .NET Client.
I'd like to apply it to a specific scenario: generating unique and total visitor counts.
A sample document that would be stored within RavenDB:
public class StatisticsEntry
{
public string Id { get; set; }
public string UserId { get; set; }
}
I can figure out how to create a standard index using Map, but I'm lost as to how to actually use the Reduce function, and then retrieve the results.
Unfortunately, the example provided on the RavenDB Site doesn't explain what's going on so that I can understand how to use it via the .NET API, and the samples don't seem to implement this at all using the .NET API.
A map reduce index is just another way of saying "I want to do a group by", only the group by is pre-defined up front and RavenDB will process it in an efficient manner in the background so at query time you are looking up a pre-calculated result.
Consider the following as an answer as an ordinary group by (for unique users)
var results = from doc in docs
group doc by doc.UserId into g
select new
{
g.UserId,
g.Count()
}
Ignoring the actual contents of the created array, we can get the total results by asking for
results.Length
as you'd expect.
In RavenDB, you split out this function into a Map and a Reduce, and you end up with
public class UniqueVisitorsResult
{
public string UserId { get; set; }
public int Count { get; set; }
}
public class UniqueVisitorsIndex : AbstractIndexCreationTask<StatisticsEntry, UniqueVisitorsResult>
{
public UniqueVisitorsIndex ()
{
Map = docs=> from doc in docs
select new
{
UserId = doc.UserId,
Count = 1
};
Reduce = results => from result in results
group result by result.UserId into g
select new
{
UserId = g.Key,
Count = g.Sum(x=>x.Count)
};
}
}
In essence, this is the same as the above - but you've turned it into a MapReduce function ;-)
session.Query<StatisticEntry, UniqueVisitorsIndex>().Count();
Will give you the total number of unique visitors, assuming Count has been implemented properly in the LINQ provider (iirc I think it has)
The total number of entries is simply
session.Query<StatisticEntry>().Count();
As you'd expect (No map/reduce required)
Note: this index can also be used to see the number of hits by a specific user, as the Count is being calculated in the index, if you don't care about the count then drop that part of the MapReduce and do
public class UniqueVisitorsIndex : AbstractIndexCreationTask<StatisticsEntry>
{
public UniqueVisitorsIndex ()
{
Map = docs=> from doc in docs
select new
{
UserId = doc.UserId
};
Reduce = results => from result in results
group result by result.UserId into g
select new
{
UserId = g.Key
};
}
}