How do I implement Denormalized References in RavenDB

ravendb

Solution

I am not sure whether this will go far enough to answer your question but here is how I go about creating a Denormalized Reference in RavenDB (this is taken from real code with non-essentials removed for clarity)

Domain

public class User : IUserIdentity
{
    public string UserName { get; set; }
    public IEnumerable<string> Claims { get; set; }
    public string Id { get; set; }
    public Guid FormsAuthenticationGuid { get; set; }
}

public class Assessment
{ 
    public string Id { get; set; }
    public UserReference User { get; set; }
    public AssessmentState State { get; set; }
}

You can see that I have a `Assessment` class that references a `User`. This user reference are managed using the `UserReference` class below.

Denormalized Reference

public class UserReference
{
    public string Id { get; set; }
    public string UserName { get; set; }

    public static implicit operator UserReference(User user)
    {
        return new UserReference
                {
                        Id = user.Id,
                        UserName = user.UserName
                };
    }
}

Note how the reference class also carries the `UserName`. This value will not change very often but it may change so we need a way to update the `UserName` property in the `UserReference` property held in the `Assessment` class. To make the change we must first find the correct `Assessment` instances from RavenDB and for that we need an index.

Raven Index

public class Assessment_ByUserId : AbstractIndexCreationTask<Assessment>
{
    public Assessment_ByUserId()
    {
        Map = assessments => from assessment in assessments
                                select new
                                    {
                                            User_Id = assessment.User.Id
                                    };
    }
}

This index needs to be invoked whenever a `User`'s `UserName` value is updated. I have a `UserService` class that helps me co-ordinate all my User related functions, so that is where I put this code.

I reuse this code for other references so it has been abstracted out a little. This may help you create the more complex hierarchies (or perhaps 'domain graph' is a better description) you want.

UserService

public static void SetUserName(IDocumentSession db, string userId, string userName)
{
    var user = db.Load<User>(userId);
    user.UserName = userName;
    db.Save(user);
    UpdateDenormalizedReferences(db, user, userName);
}

private static void UpdateDenormalizedReferences(IDocumentSession db, User user, string userName)
{
    db.Advanced.DatabaseCommands.UpdateByIndex(
            RavenIndexes.IndexAssessmentByUserId,
            GetQuery(user.Id),
            GetUserNamePatch(userName),
            allowStale: true);

}

private static IndexQuery GetQuery(string propertyValue, string propertyName = "User_Id")
{
    return new IndexQuery {Query = string.Format("{0}:{1}", propertyName, propertyValue)};
}

private static PatchRequest[] GetUserNamePatch(string referenceValue, string referenceName = "User")
{
    return new[]
            {
                    new PatchRequest
                    {
                            Type = PatchCommandType.Modify,
                            Name = referenceName,
                            Nested = new[]
                                    {
                                            new PatchRequest
                                            {
                                                    Type = PatchCommandType.Set,
                                                    Name = "UserName",
                                                    Value = referenceValue
                                            }
                                    }
                    }
            };
}

That is it. And you know, now that I lay it all out I can see what you mean. It is a lot of work just to update a reference. Perhaps the Service code can be made more DRY and reused for different relationship types, but I don't see how to get away from writing lots of indexes, one per referenced type.

Problem

I would like to have a reference between two entities stored in the RavenDB document database. Since this is not a relational db I know that I am supposed to use the Denormalized Reference technique described on RavenDBs documentation. Whilst at first this seems fine, once I start to create a real-world domain ‘hierarchy’ including bidirectional references the effort of keeping all those references up to date feels disproportionate. I feel I may be going wrong somewhere. Can you explain the best / simplest way to model a reasonably complex domain hierarchy using RavenDB? Thanks

Original source