Multi-Map Indexes
-
Multi-Map indexes allow you to index data from multiple collections, like polymorphic data or any data common to different types.
-
Learn how to index polymorphic data
Learn how to create Multi-Map-Reduce indexes -
In this page:
AddMap & AddMapForAll
The AddMap
method is used to map fields from a single collection, e.g. Dogs
.
AddMapForAll
gives you the ability to specify what fields will be indexed from a base class.
Let's assume that we have Dog
and Cat
classes, both inheriting from the class 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; }
}
We can define our index using AddMap
or AddMapForAll
and query it as follows:
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 });
}
}
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}})"
};
}
}
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();
from index 'Animals/ByName'
where Name = 'Mitzy'
Searching across multiple collections
Another great usage of Multi-Map indexes is smart-search.
To search for products, companies, or employees by their name, 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 object 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 Result
{
Id = c.Id,
Content = new[]
{
c.Name
},
DisplayName = c.Name,
Collection = MetadataFor(c)["@collection"]
});
AddMap<Product>(products => from p in products
select new Result
{
Id = p.Id,
Content = new[]
{
p.Name
},
DisplayName = p.Name,
Collection = MetadataFor(p)["@collection"]
});
AddMap<Employee>(employees => from e in employees
select new Result
{
Id = e.Id,
Content = new[]
{
e.FirstName,
e.LastName
},
DisplayName = e.FirstName + " " + e.LastName,
Collection = MetadataFor(e)["@collection"]
});
// mark 'Content' field as analyzed which enables full text search operations
Index(x => x.Content, FieldIndexing.Search);
// storing fields so when projection (e.g. ProjectInto)
// 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*")
.ProjectInto<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
Remember that all map functions must output objects
with an identical shape (the field names have to match).