Intersection

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

Lets consider a case, where we have a T-Shirt class:

public static class TShirt {
  private String id;
  private int releaseYear;
  private String manufacturer;
  private List<TShirtType> types;

  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public int getReleaseYear() {
    return releaseYear;
  }
  public void setReleaseYear(int releaseYear) {
    this.releaseYear = releaseYear;
  }
  public String getManufacturer() {
    return manufacturer;
  }
  public void setManufacturer(String manufacturer) {
    this.manufacturer = manufacturer;
  }
  public List<TShirtType> getTypes() {
    return types;
  }
  public void setTypes(List<TShirtType> types) {
    this.types = types;
  }
}

public static class TShirtType {
  private String color;
  private String size;

  public String getColor() {
    return color;
  }
  public void setColor(String color) {
    this.color = color;
  }
  public String getSize() {
    return size;
  }
  public void setSize(String size) {
    this.size = size;
  }
  public TShirtType(String color, String size) {
    super();
    this.color = color;
    this.size = size;
  }
  public TShirtType() {
    super();
  }
}

And we fill our database with few records:

TShirt tShirt1 = new TShirt();
tShirt1.setId("tshirts/1");
tShirt1.setManufacturer("Raven");
tShirt1.setReleaseYear(2010);
tShirt1.setTypes(Arrays.asList(
  new TShirtType("Blue", "Small"),
  new TShirtType("Black", "Small"),
  new TShirtType("Black", "Medium"),
  new TShirtType("Gray", "Large")));

session.store(tShirt1);

TShirt tShirt2 = new TShirt();
tShirt2.setId("tshirts/2");
tShirt2.setManufacturer("Wolf");
tShirt2.setReleaseYear(2011);
tShirt2.setTypes(Arrays.asList(
  new TShirtType("Blue", "Small"),
  new TShirtType("Black", "Large"),
  new TShirtType("Gray", "Medium")));

session.store(tShirt2);

TShirt tShirt3 = new TShirt();
tShirt3.setId("tshirts/3");
tShirt3.setManufacturer("Raven");
tShirt3.setReleaseYear(2011);
tShirt3.setTypes(Arrays.asList(
  new TShirtType("Yellow", "Small"),
  new TShirtType("Gray", "Large")));

session.store(tShirt3);

TShirt tShirt4 = new TShirt();
tShirt4.setId("tshirts/4");
tShirt4.setManufacturer("Raven");
tShirt4.setReleaseYear(2012);
tShirt4.setTypes(Arrays.asList(
  new TShirtType("Blue", "Small"),
  new TShirtType("Gray", "Large")));

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 intersect method:

QIntersection_TShirts_ByManufacturerColorSizeAndReleaseYear_Result x =
  QIntersection_TShirts_ByManufacturerColorSizeAndReleaseYear_Result.result;

List<TShirt> results = session
  .query(TShirts_ByManufacturerColorSizeAndReleaseYear.Result.class, TShirts_ByManufacturerColorSizeAndReleaseYear.class)
  .where(x.manufacturer.eq("Raven"))
  .intersect()
  .where(x.color.eq("Blue").and(x.size.eq("Small")))
  .intersect()
  .where(x.color.eq("Gray").and(x.size.eq("Large")))
  .as(TShirt.class)
  .toList();
List<TShirt> results = session
  .advanced()
  .documentQuery(TShirt.class, TShirts_ByManufacturerColorSizeAndReleaseYear.class)
  .whereEquals("Manufacturer", "Raven")
  .intersect()
  .whereEquals("Color", "Blue")
  .andAlso()
  .whereEquals("Size", "Small")
  .intersect()
  .whereEquals("Color", "Gray")
  .andAlso()
  .whereEquals("Size", "Large")
  .toList();
store
  .getDatabaseCommands()
  .query("TShirts/ByManufacturerColorSizeAndReleaseYear",
     new IndexQuery("Manufacturer:Raven INTERSECT Color:Blue AND Size:Small INTERSECT Color:Gray AND Size:Large"));
public static class TShirts_ByManufacturerColorSizeAndReleaseYear extends AbstractIndexCreationTask {
  @QueryEntity
  public static class Result {
    private String manufacturer;
    private String color;
    private String size;
    private int releaseYear;

    public String getManufacturer() {
      return manufacturer;
    }
    public void setManufacturer(String manufacturer) {
      this.manufacturer = manufacturer;
    }
    public String getColor() {
      return color;
    }
    public void setColor(String color) {
      this.color = color;
    }
    public String getSize() {
      return size;
    }
    public void setSize(String size) {
      this.size = size;
    }
    public int getReleaseYear() {
      return releaseYear;
    }
    public void setReleaseYear(int releaseYear) {
      this.releaseYear = releaseYear;
    }
  }

  public TShirts_ByManufacturerColorSizeAndReleaseYear() {
    map =
     " from tshirt in docs.Tshirts                 " +
     " from type in tshirt.Types                   " +
     " select new                                  " +
     "    {                                        " +
     "        Manufacturer = tshirt.Manufacturer,  " +
     "        Color = type.Color,                  " +
     "        Size = type.Size,                    " +
     "        ReleaseYear = tshirt.ReleaseYear     " +
     "    }; ";
  }
}

Above query will return tshirts/1 and tshirts/4 as a result. 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.