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