Multi-Map Indexes



Indexing multiple collections

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

class Dog extends Animal { }
class Cat extends Animal { }
class Animal {
    constructor(name) {
        this.name = name;
    }
}

Now we can define our index in the following way:

class Animals_ByName extends AbstractJavaScriptMultiMapIndexCreationTask  {
    constructor() {
        super();

        // Index field 'name' from the Cats collection
        this.map('Cats', cat => {
            return {
                name: cat.name
            };
        });

        // Index field 'name' from the Dogs collection
        this.map('Dogs', dog => {
            return {
                name: dog.name
            };
        });
    }
}

const results = await session
     // Query the index
    .query({ indexName: "Animals/ByName" })
     // Look for all animals (either a cat or a dog) that are named 'Mitzy' :)
    .whereEquals("name", "Mitzy")
    .all();
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 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:

class Smart_Search extends AbstractJavaScriptMultiMapIndexCreationTask  {
    constructor() {
        super();
       
        this.map('Companies', company => {
            return {
                id: id(company),
                content: company.Name,
                displayName: company.Name,
                collection: this.getMetadata(company)["@collection"]
            };
        });

        this.map('Products', product => {
            return {
                id: id(product),
                content: product.Name,
                displayName: product.Name,
                collection: this.getMetadata(product)["@collection"]
            };
        });

        this.map('Employees', employee => {
            return {
                id: id(employee),
                content: [employee.FirstName, employee.LastName],
                displayName: employee.FirstName + " " +  employee.LastName,
                collection: this.getMetadata(employee)["@collection"]
            };
        });

        // Mark the 'content' field with 'Search' to enable full-text search queries
        this.index("content", "Search");

        // Store fields in index so that when projecting these fields from the query
        // the data will come from the index, and not from the storage.
        this.store("id", "Yes");
        this.store("collection", "Yes");
        this.store("displayName", "Yes");
    }
}

and query it using:

const results = await session
    .query({ indexName: "Smart/Search" })
     // Search across all indexed collections
    .search("content", "Lau*")
     // Project results
    .selectFields([ "id", "displayName", "collection" ])
    .all();
    
// Results:
// ========
    
for (const result of results) {
    console.log(result.collection + ": " + result.displayName);
    
    // Companies: Laughing Bacchus Wine Cellars
    // Products:  Laughing Lumberjack Lager
    // Employees: Laura Callahan
}
from index "Smart/Search" 
where search(content, "Lau*")
select id() as id, displayName, collection

Remarks

Information

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