To get started using Quaere, let's begin with a simple Java program.
| Pre-beta software! Quaere has not yet been released and some of features are likely to change. At the time of writing, no binary distributions have been released. To start using Quaere today, please can follow the steps described here. |
Quaere is a query language
The Quaere query language is an internal DSL written in Java. To achieve rich language integration without the need for preprocessors, code generators or similar, Quaere employs some unconventional programming techniques. Luckily, this is tucked away inside of the Quaere API and there is only one thing you'll have to do to bring the querying abilities of Quaere to your own applications - use a static import to get the language in place.
import static org.quaere.DSL.*;
Writing our first queries
Once we have the import statement in place, we're ready to write our first query. For this introduction we'll only be using the Quaere for Objects querying engine. Keep in mind that Quaere is a query language and that these queries would look exactly the same if other data sources, such as a JPA EntityManager, where queried.
public class GettingStartedWithQuaere { public static void main() { String[] cities = { "London", "New York", "Tokyo", "Stockholm", "Barcelona", "Sydney" }; Iterable<String> allCities = from("city").in(cities).select("city"); for (String city: allCities) { System.out.println(city); } } }
When we run this program, it will print:
London New York Tokyo Stockholm Barcelona Sydney
As you can see, the Quaere query language resembles SQL, the difference is that it queries are written "backwards" - starting with the from clause and ending with a select clause. The primary reason for this is that in a Quaere query, data flows in order of the clauses. In the query above we start with defining an city alias for the cities array and then select each city into a new collection called allCities.
Let's move on to writing a query that restricts the results using a where-clause.
public class GettingStartedWithQuaere { public static void main() { Integer[] cityPopulations = { 13000000, // London 21903623, // New York 12570000, // Tokyo 1932763, // Stockholm 1605602, // Barcelona 4119190 // Sydney }; Iterable<Integer> largePopulations = from("population").in(cityPopulations). where(gt("population",10000000)) .select("population"); for (Integer population: largePopulations) { System.out.println(population); } } }
When we run this program, the populations for London, New York and Tokyo will be printed. If you're familiar with Hibernate's Criteria API, you should recognize the gt operator. Quaere uses this shorthand form for greater than comparisons. The expression in the previous sample is analogous to population > 10000000.
Our code suffers from primitive obsession, so it's time to introduce a City domain object.
// City.java public class City { private final String name; private final int population; private final Continent continent; public String getName() { return name; } public int getPopulation() { return population; } public Continent getContinent() { return continent; } public City(String name, int population, Continent continent) { this.name = name; this.population = population; this.continent = continent; } public String toString() { return String.format( "%s has a popluation of %s and is located in %s", name, population, continent); } public static final City[] ALL_CITIES={ new City("London",13000000,Continent.EUROPE), new City("New York",21903623,Continent.AMERICA), new City("Tokyo",12570000,Continent.ASIA), new City("Stockholm",1932763,Continent.EUROPE), new City("Barcelona",1605602,Continent.EUROPE), new City("Sydney",4119190,Continent.AUSTRALIA) }; } // Continent.java public enum Continent { AMERICA, EUROPE, AFRICA, ASIA, AUSTRALIA }
Now, let's change our query so that we select the names of the large cities.
public class GettingStartedWithQuaere { public static void main() { City[] cities=City.ALL_CITIES; Iterable<String> largePopulations = from("city").in(cities). where(gt("city.getPopulation()",10000000)). select("city.getName()"); for (String cityName: largePopulations) { System.out.println(cityName); } } }
When we run this program, it will print the names London, New York and Tokyo. Let's dive into what this query does. Similar to our first example, we first define an alias for the elements in the cities array, then we define a restriction predicate that says that only the cities whose population is greater than ten million should be included in the result. Notice that we refer to the getter (getPopulation()) of the City class in our predicate. Finally we select the names of the cities who matched the predicate.
Now you've learned the basics of Quaere. Let's move on to using some more features.
Unleashing the power of Quaere
Quaere supports most of the operations you're familiar with from SQL. In this section we'll learn how to use these to write more advanced queries.
Ordering
Just like with SQL, you can order the results of your queries using an order-by clause. A Quaere query results in a sequence of elements that are produced in some order that is intrinsic in the underlying information sources. Just like SQL, Quaere has operators for controlling the order of the query result. The most basic of these operators is the orderBy operator.
Let's order the cities by their population...
public class GettingStartedWithQuaere { public static void main() { City[] cities=City.ALL_CITIES; Iterable<City> largePopulations = from("city").in(cities). orderBy("city.getPopulation()"). select("city"); for (City city : largePopulations) { System.out.println(city); } } }
When we run this example, the cities will be ordered by their population in ascending order.
Barcelona has a popluation of 1605602 and is located in EUROPE Stockholm has a popluation of 1932763 and is located in EUROPE Sydney has a popluation of 4119190 and is located in AUSTRALIA Tokyo has a popluation of 12570000 and is located in ASIA London has a popluation of 13000000 and is located in EUROPE New York has a popluation of 21903623 and is located in AMERICA
Naturally, we can also sort the cities in descending order. To do this, simple change the orderBy keyword to orderByDescending.
We can also combine different ordering clauses. The example below sorts the cities by their names in ascending order and then by their population in descending order.
public class GettingStartedWithQuaere { public static void main() { City[] cities=City.ALL_CITIES; Iterable<City> largePopulations = from("city").in(cities). orderBy("city.getName()"). orderByDescending("city.getPopulation()"). select("city"); for (City city : largePopulations) { System.out.println(city); } } }
Grouping
Quaere also includes a groupBy operator which imposes a partitioning over a sequence of elements based on a key extraction function. The groupBy operator creates a Group element for each distinct key that was encountered. Each Group contains the key and the group of elements that mapped to the key.
Let's group our list of cities by continent...
public class GettingStartedWithQuaere { public static void main() { City[] cities=City.ALL_CITIES; Iterable<Group> groups = from("city").in(cities). group("city").by("city.getContinent()").into("g"). select("g"); for (Group group : groups) { System.out.println(group.getKey()); System.out.println(group.getGroup()); } } }
When we run our program, we'll get this output:
AUSTRALIA [Sydney has a popluation of 4119190 and is located in AUSTRALIA] AMERICA [New York has a popluation of 21903623 and is located in AMERICA] EUROPE [London has a popluation of 13000000 and is located in EUROPE, Stockholm has a popluation of 1932763 and is located in EUROPE, Barcelona has a popluation of 1605602 and is located in EUROPE] ASIA [Tokyo has a popluation of 12570000 and is located in ASIA]
Notice that the query produces a sequence of Group instances rather than City instances. The Group class is a special purpose class used to demote groups.
package org.quaere; import java.util.List; import java.util.ArrayList; public class Group { private final Object key; private final List<Object> group = new ArrayList<Object>(); public Group(Object key) { this.key = key; } public List<Object> getGroup() { return group; } public Object getKey() { return key; } }

