Skip to content

Instantly share code, notes, and snippets.

@PaulStovell
Created March 31, 2012 11:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PaulStovell/2262298 to your computer and use it in GitHub Desktop.
Save PaulStovell/2262298 to your computer and use it in GitHub Desktop.
Hierarchies in RavenDB
public class Group
{
public Group()
{
Users = new List<string>();
ChildGroups = new List<string>();
}
public string Id { get; set; }
public string Name { get; set; }
public IList<string> Users { get; set; }
public IList<string> ChildGroups { get; set; }
}
// This index inverts the relationship between groups and child groups into groups and their direct parents
// E.g., from this:
// { All users, [ Technical, Management ] }
// { Technical, [ Developers, Release Managers ] }
// { Super Users, [ Release Managers ] }
// We get this:
// { Technical, [ All users ] }
// { Management, [ All users ] }
// { Developers, [ Technical ] }
// { Release Managers, [ Technical, Super Users ] }
public class GroupDirectParents : AbstractIndexCreationTask<Group, GroupDirectParents.Result>
{
public GroupDirectParents()
{
Map = groups => from g in groups
from child in g.ChildGroups
select new Result { GroupId = child, ParentGroupIds = new[] { g.Id } };
Reduce = results => from r in results
group r by r.GroupId into g
select new Result { GroupId = g.Key, ParentGroupIds = g.SelectMany(i => i.ParentGroupIds).ToArray() };
}
public class Result
{
public string GroupId { get; set; }
public string[] ParentGroupIds { get; set; }
}
}
// This index is similar to above. However, it will work recursively such that it returns all parent groups of a group
// E.g., from this:
// { All users, [ Technical, Management ] }
// { Technical, [ Developers, Release Managers ] }
// { Super users, [ Release Managers ] }
// We get this:
// { Technical, [ All users ] }
// { Management, [ All users ] }
// { Developers, [ Technical, All users ] }
// { Release Managers, [ Technical, Super users, All users ] }
public class GroupIndirectParents : AbstractIndexCreationTask<Group, GroupIndirectParents.Result>
{
public GroupIndirectParents()
{
Map = ??
Reduce = ??
}
public class Result
{
public string GroupId { get; set; }
public string[] ParentGroupIds { get; set; }
}
}
[TestFixture]
public class Tests
{
private DocumentStore documentStore;
[SetUp]
public void SetUp()
{
documentStore = new EmbeddableDocumentStore { RunInMemory = true };
documentStore.Initialize();
IndexCreation.CreateIndexes(typeof(GroupDirectParents).Assembly, documentStore);
// Build a tree of groups -
// G: Everyone
// G: Characters
// G: Male
// G: Females
using (var session = documentStore.OpenSession())
{
session.Store(new Group(), "groups/everyone");
session.Store(new Group(), "groups/characters");
session.Store(new Group(), "groups/males");
session.Store(new Group(), "groups/females");
session.SaveChanges();
session.Load<Group>("groups/everyone").ChildGroups.Add("groups/characters");
session.Load<Group>("groups/characters").ChildGroups.Add("groups/males");
session.Load<Group>("groups/characters").ChildGroups.Add("groups/females");
session.SaveChanges();
}
}
// This test passes
[Test]
public void ShouldFindDirectParentsForGroups()
{
using (var session = documentStore.OpenSession())
{
// This index gives us the first parent group of each group
var results = session.Query<GroupDirectParents.Result, GroupDirectParents>().Customize(c => c.WaitForNonStaleResultsAsOfNow(TimeSpan.FromSeconds(30))).ToList();
AssertGroupHasParents(results, "groups/males", "groups/characters");
AssertGroupHasParents(results, "groups/females", "groups/characters");
}
}
// This test doesn't pass, but I want it to
[Test]
public void ShouldFindIndirectParentsForGroups()
{
using (var session = documentStore.OpenSession())
{
// This index gives us the *all* parent groups of each group, both direct parents and all ancestors
var results = session.Query<GroupIndirectParents.Result, GroupIndirectParents>().Customize(c => c.WaitForNonStaleResultsAsOfNow(TimeSpan.FromSeconds(30))).ToList();
// Note that in this case, we're testing on the parent of a parent
AssertGroupHasParents(results, "groups/males", "groups/characters", "groups/everyone");
AssertGroupHasParents(results, "groups/females", "groups/characters", "groups/everyone");
AssertGroupHasParents(results, "groups/characters", "groups/everyone");
}
}
private void AssertGroupHasParents(IEnumerable<GroupDirectParents.Result> results, string group, params string[] parents)
{
var groupParents = results.FirstOrDefault(r => r.GroupId == group);
Assert.IsNotNull(groupParents, "Could not find an index record for group: " + group);
CollectionAssert.AreEquivalent(parents, groupParents.ParentGroupIds);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment