Querying: Intersection

To allow users to intersect queries on the server-side and return only documents that match all the provided sub-queries, we introduced the query intersection feature.

Let's consider a case where we have a T-Shirt class:

class TShirt {
    constructor(data) {
        this.id = data.id;
        this.manufacturer = data.manufacturer;
        this.releaseYear = data.releaseYear;
        this.types = data.types;
    }
}

class TShirtType {
    constructor(color, size) {
        this.color = color;
        this.size = size;
    }
}

We will fill our database with few records:

const tShirt1 = new TShirt({
    id: "tshirts/1",
    manufacturer: "Raven",
    releaseYear: 2010,
    types: [
        new TShirtType("Blue", "Small"),
        new TShirtType("Black", "Small"),
        new TShirtType("Black", "Medium"),
        new TShirtType("Gray", "Large")
    ]
});
await session.store(tShirt1);

const tShirt2 = new TShirt({
    id: "tshirts/2",
    manufacturer: "Wolf",
    releaseYear: 2011,
    types: [
        new TShirtType("Blue", "Small"),
        new TShirtType("Black", "Large"),
        new TShirtType("Gray", "Medium")
    ]
});
await session.store(tShirt2);

const tShirt3 = new TShirt({
    id: "tshirts/3",
    manufacturer: "Raven",
    releaseYear: 2011,
    types: [
        new TShirtType("Yellow", "Small"),
        new TShirtType("Gray", "Large")
    ]
});
await session.store(tShirt3);

const tShirt4 = new TShirt({
    id: "tshirts/4",
    manufacturer: "Raven",
    releaseYear: 2012,
    types: [
        new TShirtType("Blue", "Small"),
        new TShirtType("Gray", "Large")
    ]
});
await session.store(tShirt4);

Now we want to return all the T-shirts that are manufactured by Raven and contain both Small Blue and Large Gray types.

To do this, we need to use the intersect query method:

const result = await session.query({ 
        indexName: "TShirts/ByManufacturerColorSizeAndReleaseYear"
    })
    .whereEquals("manufacturer", "Raven")
    .intersect()
    .whereEquals("color", "Blue")
    .andAlso()
    .whereEquals("size", "Small")
    .intersect()
    .whereEquals("color", "Gray")
    .andAlso()
    .whereEquals("size", "large")
    .ofType(TShirts_ByManufacturerColorSizeAndReleaseYearResult)
    .all();
class TShirts_ByManufacturerColorSizeAndReleaseYearResult {
    constructor(data) {
        this.manufacturer = data.manufacturer;
        this.color = data.color;
        this.size = data.size;
        this.releaseYear = data.releaseYear;
    }
}

class TShirts_ByManufacturerColorSizeAndReleaseYear extends AbstractIndexCreationTask {

    constructor() {
        super();

        this.map = `docs.TShirts.SelectMany(tshirt => tshirt.types, (tshirt, type) => new {
                manufacturer = tshirt.manufacturer,
                color = type.color,
                size = type.size,
                releaseYear = tshirt.releaseYear
            })`;
    }
}
from index 'TShirts/ByManufacturerColorSizeAndReleaseYear' 
where intersect(manufacturer = 'Raven', color = 'Blue' and size = 'Small', color = 'Gray' and size = 'Large')

The above query will return tshirts/1 and tshirts/4 as a result. The document tshirts/2 will not be included because it is not manufactured by Raven, and tshirts/3 is not available in Small Blue so it does not match all the sub-queries.