Multi-Map Indexes


AddMap

The AddMap method is used to map fields from a single collection, e.g. Dogs. Let's assume that we have Dog and Cat classes, both inheriting 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 and query it as follows:

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'

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

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