You are currently browsing legacy 4.1 version of documentation. Click here to switch to the newest 4.2 version.

We can help you with migration to the latest RavenDB

Contact Us Now
see on GitHub

Indexes: 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. 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, and both of them inherit 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; }
}

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 });
    }
}
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'

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. 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 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)["@collection"]
            });

        AddMap<Product>(products => from p in products
            select new
            {
                p.Id,
                Content = new[]
                {
                    p.Name
                },
                DisplayName = p.Name,
                Collection = MetadataFor(p)["@collection"]
            });

        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)["@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

Information

Remember that all map functions must output objects with identical shape (field names have to match).