Filtering
One of the most basic functionalities when it comes to querying is the ability to filter out data and only return records that match given condition. There are couple of ways to do this, and they all depend on querying approach you want to use (Query from basic session operations, DocumentQuery from advanced session operations or low-level Command). Following example demonstrates how to add a simple conditions to query using all those methods.
Where
QEmployee e = QEmployee.employee;
List<Employee> results = session
.query(Employee.class, Employees_ByFirstAndLastName.class) //query 'Employees/ByFirstAndLastName' index
.where(e.firstName.eq("Robert").and(e.lastName.eq("King"))) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QEmployee e = QEmployee.employee;
List<Employee> results = session
.advanced()
.documentQuery(Employee.class, Employees_ByFirstAndLastName.class) // query 'Employees/ByFirstAndLastName' index
.whereEquals(e.firstName, "Robert") // filtering predicates
.andAlso() // by default OR is between each condition
.whereEquals(e.lastName, "King") // filtering predicates
.toList(); // materialize query by sending it to server for processing
QueryResult result = store
.getDatabaseCommands()
.query("Employees/ByFirstAndLastName",
new IndexQuery("FirstName:Robert AND LastName:King"));
public Employees_ByFirstAndLastName() {
map =
" from employee in docs.Employees " +
" select new " +
" { " +
" FirstName = employee.FirstName, " +
" LastName = employee.LastName " +
" }; ";
}
Where - numeric property
QProduct p = QProduct.product;
List<Product> results = session
.query(Product.class, Products_ByUnitsInStock.class) // query 'Products/ByUnitsInStock' index
.where(p.unitsInStock.gt(10)) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QProduct p = QProduct.product;
List<Product> results = session
.advanced()
.documentQuery(Product.class, Products_ByUnitsInStock.class) // query 'Products/ByUnitsInStock' index
.whereGreaterThan(p.unitsInStock, 50) // filtering predicates
.toList(); // materialize query by sending it to server for processing
store
.getDatabaseCommands()
.query("Products/ByUnitsInStock",
new IndexQuery("UnitsInStock_Range:{Ix50 TO NULL}"));
public static class Products_ByUnitsInStock extends AbstractIndexCreationTask {
public Products_ByUnitsInStock() {
map =
" from product in docs.Products " +
" select new " +
" { " +
" product.UnitsInStock " +
" }; ";
}
}
The importance of types in queries
Let's consider the following index and queries:
public static class Orders_ByTotalPrice extends AbstractIndexCreationTask {
@QueryEntity
public static class Result {
public double totalPrice;
}
public Orders_ByTotalPrice() {
map = "from order in docs.orders select new { TotalPrice = order.Lines.Sum(x => (x.Quantity * x.PricePerUnit) * (1 - x.Discount)) }";
}
}
QFiltering_Orders_ByTotalPrice_Result x = QFiltering_Orders_ByTotalPrice_Result.result;
List<Orders_ByTotalPrice.Result> results = session.query(Orders_ByTotalPrice.Result.class, Orders_ByTotalPrice.class)
.where(x.totalPrice.gt(50))
.toList();
QFiltering_Orders_ByTotalPrice_Result x = QFiltering_Orders_ByTotalPrice_Result.result;
List<Orders_ByTotalPrice.Result> results = session.advanced().documentQuery(Orders_ByTotalPrice.Result.class, Orders_ByTotalPrice.class)
.whereGreaterThan(x.totalPrice, 50.0)
.toList();
store.getDatabaseCommands().query("Orders/ByTotalPrice", new IndexQuery("TotalPrice_Range:{Dx50 TO NULL}"));
Note that PricePerUnit
is of type double
in Order
entity, so indexed TotalPrice
value will be double
too. In oder to
properly query such index, we need to preserve types in queries. That is why Orders_ByTotalPrice.Result.TotalPrice
is double
and
IndexQuery.Query
has Dx
prefix specified before the actual value. Types of properties in predicates have to match types of indexed fields.
- More about the need to use
OfType
here, you can find in projections article.
Where - nested property
QOrder o = QOrder.order;
List<Order> results = session
.query(Order.class, Order_ByOrderLinesCount.class) // query 'Order/ByOrderLinesCount' index
.where(o.lines.size().lt(50)) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QOrder o = QOrder.order;
session
.advanced()
.documentQuery(Order.class, Order_ByOrderLinesCount.class) // query 'Order/ByOrderLinesCount' index
.whereGreaterThan(o.lines.size(), 50) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QueryResult result = store
.getDatabaseCommands()
.query("Order/ByOrderLinesCount",
new IndexQuery("Lines.Count_Range:{Ix50 TO NULL}"));
public static class Order_ByOrderLinesCount extends AbstractIndexCreationTask {
public Order_ByOrderLinesCount() {
map =
" from order in docs.Orders " +
" select new " +
" { " +
" Lines_Count = order.Lines.Count " +
" };";
}
}
Where + Any
Any
is useful when you have a collection of items (e.g. Order
contains OrderLines
) and you want to filter out based on a values from this collection. For example, let's retrieve all orders that contain a OrderLine
with a given product.
QOrder o = QOrder.order;
QOrderLine l = QOrderLine.orderLine;
List<Order> results = session
.query(Order.class, Order_ByOrderLines_ProductName.class) // query 'Order/ByOrderLines/ProductName' index
.where(o.lines.any(l.productName.eq("Teatime Chocolate Biscuits"))) // filtering predicates
.toList(); // materialize query by sending it to server for processing
List<Order> results = session
.advanced()
.documentQuery(Order.class, Order_ByOrderLines_ProductName.class) // query 'Order/ByOrderLines/ProductName' index
.whereEquals("Lines,ProductName", "Teatime Chocolate Biscuits") // filtering predicates
.toList(); // materialize query by sending it to server for processing
QueryResult result = store
.getDatabaseCommands()
.query("Order/ByOrderLinesCount",
new IndexQuery("Lines,ProductName:\"Teatime Chocolate Biscuits\""));
public static class Order_ByOrderLines_ProductName extends AbstractIndexCreationTask {
public Order_ByOrderLines_ProductName() {
map =
" from order in docs.Orders " +
" select new " +
" { " +
" Lines_ProductName = order.Lines.Select(x => x.ProductName) " +
" }; ";
}
}
Where + In
When you want to check single value against multiple values in
operator can become handy. E.g. to retrieve all employees that FirstName
is either Robert
or Nancy
we can issue following query:
QEmployee e = QEmployee.employee;
List<Employee> results = session
.query(Employee.class, Employees_ByFirstAndLastName.class) // query 'Employees/ByFirstAndLastName' index
.where(e.firstName.in("Robert", "Nancy")) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QEmployee e = QEmployee.employee;
List<Employee> results = session
.advanced()
.documentQuery(Employee.class, Employees_ByFirstAndLastName.class) // query 'Employees/ByFirstAndLastName' index
.whereIn(e.firstName, Arrays.asList("Robert", "Nancy")) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QueryResult result = store
.getDatabaseCommands()
.query("Employees/ByFirstAndLastName",
new IndexQuery("@in<FirstName>:(Robert, Nancy)"));
public Employees_ByFirstAndLastName() {
map =
" from employee in docs.Employees " +
" select new " +
" { " +
" FirstName = employee.FirstName, " +
" LastName = employee.LastName " +
" }; ";
}
Where + ContainsAny
To check if enumeration contains any of the values from a specified collection you can use containsAny
method.
Let's assume that we want to return all BlogPosts
that contain any of the specified Tags
.
QBlogPost b = QBlogPost.blogPost;
List<BlogPost> results = session
.query(BlogPost.class, BlogPosts_ByTags.class) // query 'BlogPosts/ByTags' index
.where(b.tags.containsAny(Arrays.asList("Development", "Research"))) // filtering predicates
.toList(); // materialize query by sending it to server for processing
List<BlogPost> results = session
.advanced()
.documentQuery(BlogPost.class, BlogPosts_ByTags.class) // query 'BlogPosts/ByTags' index
.containsAny("Tags", Arrays.<Object>asList("Development", "Research")) // filtering predicates
.toList(); // materialize query by sending it to server for processing
QueryResult result = store
.getDatabaseCommands()
.query("BlogPosts/ByTags",
new IndexQuery("(Tags:Development OR Tags:Research)"));
public static class BlogPosts_ByTags extends AbstractIndexCreationTask {
public BlogPosts_ByTags() {
map =
" from post in docs.Posts " +
" select new " +
" { " +
" post.Tags " +
" };";
}
}
Where + ContainsAll
To check if enumeration contains all of the values from a specified collection you can use containsAll
method.
Let's assume that we want to return all BlogPosts
that contain all of the specified Tags
.
QFiltering_Orders_ByTotalPrice_Result r = QFiltering_Orders_ByTotalPrice_Result.result;
List<Orders_ByTotalPrice.Result> results = session
.query(Orders_ByTotalPrice.Result.class, Orders_ByTotalPrice.class).where(r.totalPrice.gt(50)).toList();
QFiltering_Orders_ByTotalPrice_Result r = QFiltering_Orders_ByTotalPrice_Result.result;
List<Orders_ByTotalPrice.Result> results = session.advanced()
.documentQuery(Orders_ByTotalPrice.Result.class, Orders_ByTotalPrice.class)
.whereGreaterThan(r.totalPrice, 50.0).toList();
store.getDatabaseCommands().query("Orders/ByTotalPrice", new IndexQuery("TotalPrice_Range:{Dx50 TO NULL}"));
public static class BlogPosts_ByTags extends AbstractIndexCreationTask {
public BlogPosts_ByTags() {
map =
" from post in docs.Posts " +
" select new " +
" { " +
" post.Tags " +
" };";
}
}
Remarks
Information
Underneath, Query
and DocumentQuery
are converting predicates to IndexQuery
class so they can issue a query from low-level command method.
Safe By Default
By default page size is set to 128 if not specified, so above queries will not return more than 128 results. You can read more about paging here.