Indexes: Indexing Related Documents

To extend indexing capabilities and simplify many scenarios, we have introduced the possibility for indexing related documents.

Example I

Let's consider a simple Product - Category scenario where you want to look for a Product by Category Name.

Without this feature, you would have to create a fairly complex multiple map-reduce index. This is why the LoadDocument function was introduced.

class Products_ByCategoryName extends AbstractIndexCreationTask {
    constructor() {
        super();

        this.map = `docs.Products.Select(product => new {     
            CategoryName = (this.LoadDocument(product.Category, "Categories")).Name 
        })`;
    }
}
const indexDefinition = new IndexDefinition();
indexDefinition.name = "Products/ByCategoryName";
indexDefinition.maps = new Set([
    `from product in products    
     select new {        
         CategoryName = LoadDocument(product.Category, ""Categories"").name    
    }`]);

await store.maintenance.send(new PutIndexesOperation(indexDefinition));

Now we will be able to search for products using the categoryName as a parameter:

const results = session
    .query({ indexName: "Products/ByCategoryName" })
    .whereEquals("CategoryName", "Beverages")
    .ofType(Product)
    .all();

Example II

Our next scenario will show us how indexing of more complex relationships is also trivial. Let's consider the following case:

class Book {
    constructor(id, name) {
        this.id = id;
        this.name = name;
    }
}

class Author {
    constructor(id, name, bookIds) {
        this.id = id;
        this.name = name;
        this.bookIds = bookIds;
    }
}

To create an index with Author Name and list of Book Names, we need do the following:

class Authors_ByNameAndBooks extends AbstractIndexCreationTask {
    constructor() {
        super();

        this.map = `docs.Authors.Select(author => new {     
            name = author.name,     
            books = author.bookIds.Select(x => (this.LoadDocument(x, "Books")).name) 
        })`;

    }
}
const indexDefinition = new IndexDefinition();
indexDefinition.name = "Authors/ByNameAndBooks";
indexDefinition.maps = new Set([
    `from author in docs.Authors      
     select new 
     {          
         name = author.name,          
         books = author.bookIds.Select(x => LoadDocument(x, ""Books"").id)      
     }`
]);

await store.maintenance.send(new PutIndexesOperation(indexDefinition));

const results = await session
    .query({ indexName: "Authors/ByNameAndBooks" })
    .whereEquals("name", "Andrzej Sapkowski")
    .whereEquals("books", "The Witcher")
    .ofType(Author)
    .all();

Remarks

Information

Indexes are updated automatically when related documents change.

Warning

Using the LoadDocument adds a loaded document to the tracking list. This may cause very expensive calculations to occur, especially when multiple documents are tracking the same document.