Multi-Map indexes
Multi-Map indexes allow you to index data from multiple collections e.g. polymorphic data (check the example) or any common data between types.
AddMap & AddMapForAll
AddMap
method is used to map fields from a single collection e.g. Dogs
. On the other hand, AddMapForAll
gives you the ability to specify what fields will be indexed from base class.
Let's assume that we have a Dog
and a Cat
classes, and both of them inherit from the Animal
:
public class Dog : Animal
{
}
public class Cat : Animal
{
}
public abstract class Animal : IAnimal
{
public string Name { get; set; }
}
public interface IAnimal
{
string Name { get; set; }
}
Now we can define our index using AddMap
or AddMapForAll
in the following way:
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_ForAll : AbstractMultiMapIndexCreationTask
{
public Animals_ByName_ForAll()
{
AddMapForAll<Animal>(parents => from p in parents select new { p.Name });
}
}
IList<IAnimal> results = session
.Query<IAnimal, Animals_ByName>()
.Where(x => x.Name == "Mitzy")
.ToList();
IList<IAnimal> results = session
.Advanced
.DocumentQuery<IAnimal, Animals_ByName>()
.WhereEquals(x => x.Name, "Mitzy")
.ToList();
QueryResult result = store
.DatabaseCommands
.Query(
"Animals/ByName",
new IndexQuery
{
Query = "Name:Mitzy"
});
Indexing polymorphic data
Please read more in our dedicated article on indexing polymorphic data. This article can be found here.
Searching across multiple collections
Another great application of Multi-Map indexes is smart-search. Imagine that you want to search for products, companies, or employees by their name. In order to do it, you need to define the following index:
public class Smart_Search : AbstractMultiMapIndexCreationTask<Smart_Search.Result>
{
public class Result
{
public string Id { get; set; }
public string DisplayName { get; set; }
public string Collection { get; set; }
public string Content { get; set; }
}
public class Projection
{
public string Id { get; set; }
public string DisplayName { get; set; }
public string Collection { get; set; }
}
public Smart_Search()
{
AddMap<Company>(companies => from c in companies
select new
{
c.Id,
Content = new[]
{
c.Name
},
DisplayName = c.Name,
Collection = MetadataFor(c)["Raven-Entity-Name"]
});
AddMap<Product>(products => from p in products
select new
{
p.Id,
Content = new[]
{
p.Name
},
DisplayName = p.Name,
Collection = MetadataFor(p)["Raven-Entity-Name"]
});
AddMap<Employee>(employees => from e in employees
select new
{
e.Id,
Content = new[]
{
e.FirstName,
e.LastName
},
DisplayName = e.FirstName + " " + e.LastName,
Collection = MetadataFor(e)["Raven-Entity-Name"]
});
// mark 'Content' field as analyzed which enables full text search operations
Index(x => x.Content, FieldIndexing.Analyzed);
// storing fields so when projection (e.g. ProjectFromIndexFieldsInto)
// requests only those fields
// then data will come from index only, not from storage
Store(x => x.Id, FieldStorage.Yes);
Store(x => x.DisplayName, FieldStorage.Yes);
Store(x => x.Collection, FieldStorage.Yes);
}
}
and query it using:
IList<Smart_Search.Projection> results = session
.Query<Smart_Search.Result, Smart_Search>()
.Search(x => x.Content, "Lau*", escapeQueryOptions: EscapeQueryOptions.AllowPostfixWildcard)
.ProjectFromIndexFieldsInto<Smart_Search.Projection>()
.ToList();
foreach (Smart_Search.Projection result in results)
{
Console.WriteLine(result.Collection + ": " + result.DisplayName);
// Companies: Laughing Bacchus Wine Cellars
// Products: Laughing Lumberjack Lager
// Employees: Laura Callahan
}
Remarks
Information
Remember that all map functions must output objects with identical shape (field names have to match).