Multi-Map Indexes


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).