Indexing Polymorphic Data
-
By default, RavenDB indexes are defined on a specific entity type, referred to as a
Collection
,
and do not consider the inheritance hierarchy. -
In this Page:
Polymorphic Data
Let's assume, for example, that we have the following inheritance hierarchy:
When saving a Cat
document, it will be assigned to the "Cats" collection,
while a Dog
document will be placed in the "Dogs" collection.
If we intend to create a simple Map-index for Cat documents based on their names, we would write:
from cat in docs.Cats
select new { cat.Name }
And for dogs:
from dog in docs.Dogs
select new { dog.Name }
The challenge
Querying each index results in documents only from the specific collection the index was defined for.
However, what if we need to query across ALL animal collections?
Multi-Map Indexes
The easiest way to do this is by writing a multi-map index such as:
public class Animals_ByName : AbstractMultiMapIndexCreationTask
{
public Animals_ByName()
{
AddMap<Cat>(cats => from c in cats select new { c.Name });
AddMap<Dog>(dogs => from d in dogs select new { d.Name });
}
}
public class Animals_ByName : AbstractJavaScriptIndexCreationTask
{
public Animals_ByName()
{
Maps = new HashSet<string>()
{
@"map('cats', function (c){ return {Name: c.Name}})",
@"map('dogs', function (d){ return {Name: d.Name}})"
};
}
}
And query it like this:
IList<IAnimal> results = session
.Query<IAnimal, Animals_ByName>()
.Where(x => x.Name == "Mitzy")
.ToList();
List<IAnimal> results = session
.Advanced
.DocumentQuery<IAnimal, Animals_ByName>()
.WhereEquals("Name", "Mitzy")
.ToList();
from index 'Animals/ByName'
where Name = 'Mitzy'
Other Options
Another option would be to modify the way we generate the Collection for subclasses of Animal
:
DocumentStore store = new DocumentStore()
{
Conventions =
{
FindCollectionName = type =>
{
if (typeof(Animal).IsAssignableFrom(type))
return "Animals";
return DocumentConventions.DefaultGetCollectionName(type);
}
}
};
Using this method, we can now index on all animals using:
from animal in docs.Animals
select new { animal.Name }
But what happens when you don't want to modify the entity name of an entity itself?
You can create a polymorphic index using:
from animal in docs.WhereEntityIs("Cats", "Dogs")
select new { animal.Name }
It will generate an index that matches both Cats and Dogs.