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 a
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
};
}
}
Here is how you can build an index for unique visitors:
public class Statistics_UniqueVisitors : AbstractIndexCreationTask<StatisticsEntry>
{
public Statistics_UniqueVisitors()
{
Map = entries => from entry in entries
select new { entry.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) };
}
}
You can then query this using:
var numberOfUniqueVisitors = s.Query<StatisticEntry, Statistics_UniqueVisitors>().Count();
For total count of visitors, you can use:
var numberOfVisitors = s.Query<StatisticEntry>().Count();