At the beginning of Java programming, each of us encountered the problem of filtering the contents of a collection. We need to filter the data more than once or twice in our codes. It’s necessary when we want to do something with them and process them properly.
Java and collection filtering
The era of lambda expressions and some simplifications of – we can model conditional loops – made our lives easier. Java started to be more readable for beginners. However, there remains one aspect that the Java standard does not have – advanced filtering of stream elements similar to LINQ, which is part of the C# language standard.
LINQ – simple query generation
LINQ is nothing more than a system that allows the simple creation of queries to objects. And this is without the need to query a real database using SQL/no-SQL queries. They are very intuitive. In a short time, users can easily model the appropriate queries for the resources they need.
CQEngine – quick collection filtering
Recently I came across a library known as CQEngine. It allows you to replicate the same functionality in Java with pretty good performance. CQEngine was created by Niall Gallagher. Jetbrains was a patron of this project. CQEngine, besides calling methods corresponding to operations, allows you to write SQL-like queries in your notation. Then, they are interpreted by the library in the appropriate query.
Queries and collections creation
CQEngine allows not only to create LINQ queries but also to optionally define and build indexes (these are objects that are used in the database to improve the speed of access to data).
The indexes themselves are divided into sub-types that allow you to specify how specific fields are to be searched.
To get started with CQEngine, we can import our library using maven:
Additionally, I recommend adding the Typetools dependency, which allows us to process attributes in lambda format:
We will embed the example queries on the following data model:
We defined a Student class and a Course class in our test case. They contain basic information about students and the courses they are currently taking.
To be able to search our collections, we need to define attributes. We can let the class do this automatically by:
In addition, it will not take into account the generation of attributes for collections found in our class definitions. Hence in the example, we have explicitly defined what we want to query and under what name.
With the use of attributes for internal collections comes the need to write dedicated linkers from the main class. We can use MultiValueAttribute for this with appropriate mapping to the class attribute we are interested in. Additionally, @Value from the Lombok package is used to improve readability.
Our examples of filtering collections
We will base our examples on the following list of students:
Unlike a typical database, we can’t dynamically query the attributes of a subclass from the parent class within the library other than by defining an attribute that „passes” the values we are interested in into the queryable ones. We can see below the modeling of such an attribute:
along with its use within a test:
Here are some smaller examples of using the functions provided by the library:
The ability to nest functions allows us to model a query with subqueries. Each function execution returns a ResultSet. We can then map it to the collection we want or further process it as part of lambda expressions.
The query results themselves can be sorted after their execution also. We can see that in the case of „queryFour”. The filtered result was sorted in descending order based on the index numbers.
SQL-like and query modeling
In the case of „SQL-like” syntax, we can model queries in a function-like form and continue to operate within the attributes we set.
CQN and Variable Searching
The library also provides the ability to search variables using CQN syntax – allowing for nested conditions while maintaining readability.
You can find a more detailed library description on the CQEngine project page.