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

AddMap method is used to map fields from a single collection e.g. Dogs.

Let's assume that we have Dog and Cat classes, and both of them inherit from the class Animal:

public static class Dog extends Animal {

}
public static class Cate extends Animal {

}
public abstract static class Animal implements IAnimal {
    private String name;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }
}
public interface IAnimal {
    String getName();
    void setName(String name);
}

Now we can define our index using addMap in the following way:

public static class Animals_ByName extends AbstractMultiMapIndexCreationTask {
    public Animals_ByName() {
        addMap( "docs.Cats.Select(c => new { " +
            "    Name = c.Name " +
            "})");

        addMap( "docs.Dogs.Select(d => new { " +
            "    Name = d.Name " +
            "})");
    }
}
public static class Animals_ByName extends AbstractJavaScriptIndexCreationTask {
    public Animals_ByName() {
        setMaps(Sets.newHashSet(
            "map('cats', function (c){ return {name: c.name}})",
            "map('dogs', function (d){ return {name: d.name}})"
        ));
    }
}

List<IAnimal> results = session
    .query(IAnimal.class, Animals_ByName.class)
    .whereEquals("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 static class Smart_Search extends AbstractMultiMapIndexCreationTask {
    public static class Result {
        private String id;
        private String displayName;
        private String collection;
        private String content;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getDisplayName() {
            return displayName;
        }

        public void setDisplayName(String displayName) {
            this.displayName = displayName;
        }

        public String getCollection() {
            return collection;
        }

        public void setCollection(String collection) {
            this.collection = collection;
        }

        public String getContent() {
            return content;
        }

        public void setContent(String content) {
            this.content = content;
        }
    }

    public static class Projection {
        private String id;
        private String displayName;
        private String collection;

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getDisplayName() {
            return displayName;
        }

        public void setDisplayName(String displayName) {
            this.displayName = displayName;
        }

        public String getCollection() {
            return collection;
        }

        public void setCollection(String collection) {
            this.collection = collection;
        }
    }

    public Smart_Search() {

        addMap("docs.Companies.Select(c => new { " +
            "    Id = Id(c), " +
            "    Content = new string[] { " +
            "        c.Name " +
            "    }, " +
            "    DisplayName = c.Name, " +
            "    Collection = this.MetadataFor(c)[\"@collection\"] " +
            "})");

        addMap("docs.Products.Select(p => new { " +
            "    Id = Id(p), " +
            "    Content = new string[] { " +
            "        p.Name " +
            "    }, " +
            "    DisplayName = p.Name, " +
            "    Collection = this.MetadataFor(p)[\"@collection\"] " +
            "})");

        addMap("docs.Employees.Select(e => new { " +
            "    Id = Id(e), " +
            "    Content = new string[] { " +
            "        e.FirstName, " +
            "        e.LastName " +
            "    }, " +
            "    DisplayName = (e.FirstName + \" \") + e.LastName, " +
            "    Collection = this.MetadataFor(e)[\"@collection\"] " +
            "})");

        // mark 'content' field as analyzed which enables full text search operations
        index("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("Id", FieldStorage.YES);
        store("DisplayName", FieldStorage.YES);
        store("Collection", FieldStorage.YES);
    }
}

and query it using:

List<Smart_Search.Projection> results = session
    .query(Smart_Search.Result.class, Smart_Search.class)
    .search("Content", "Lau*")
    .selectFields(Smart_Search.Projection.class)
    .toList();

for (Smart_Search.Projection result : results) {
    System.out.println(result.getCollection() + ": " + result.getDisplayName());
    // 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).