Gather
Gather
is an ultra-lightweight, 37KB, no dependencies library to fire SQL like queries on Java collections and arrays. The data is not inserted into any in-memory database, nor any index is created. The query is matched against all the objects (could be different types as well) against the provided query. Useful to run ad-hoc queries and aggregation functions, say on XML/JSON/CSV and more.
The library is tested on the following JDK versions:
- Oracle JDK 9
- Oracle JDK 8
- Oracle JDK 7
Table of Contents
- Usecases
- Usage Examples
- Composed Objects and Keys
- Features
- Roadmap
- Clause chaining and Evaluation
- Performance
- Downloads
- Release Notes
- Versioning
- License
Usecases
The library is useful in the following scenarios:
- Run ad-hoc queries during development to debug data-sets and tweak DB queries
- Run ad-hoc queries on production data via some admin interface
- Query on objects of different types in one-shot, say
List<Object>
thanList<Employee>
- Works with JDK 9, 8, 7 and legacy code
Usage Examples
List<Employee> employees = this.employeeService.getAllEmployeesFromDatabase();
// prepare a query
Gather query = Gather.where("age").greaterThan(50).and("status").is("active");
// fire query
List<Employee> filtered = query.find(employees);
// reuse the query for another collection
List<Manager> managers = this.employeeService.getAllManagersFromDatabase();
List<Manager> filteredManagers = query.find(managers);
// null check operations
query = Gather.where("name").isNull();
query = Gather.where("name").isNotNull();
// not operator
query = Gather.where("name").not().is("sandeep");
// find instances that have a property
// one of the two ways can be employed
gather = Gather.where("name").existsProperty();
gather = Gather.hasProperty("name");
// query if an attribute which is a collection or an array contains a given value
// converting between various primitive value types is supported
gather = Gather.where("someFloatArray").has(new Double(123));
// run over collections as well
gather = Gather.where("collection").has(objectInstance);
// find a single instance
query.findOne(employees);
// find limited instances
query.find(employees, 5);
// find 5 instances, but skip the first 10 instances
query.find(employees, 5, 10);
// count the number of results rather than accumulating them
// this is much faster and memory efficient
int numResults = query.count(employees);
Composed Objects and Keys
Gather
supports composed objects and keys in the where
clause based on them. For example: the following query will search all objects in the collection/array called children
which have an age
attribute and its value is less than 40
.
Gather query = Gather.where("children.age").lessThan(10);
query.find(employees);
public class Employee {
public List<Child> children;
}
public class Child {
public int age;
}
Features
- Gathering operations
find
- find all matching objects, with options to skip elements and/or limit number of matchesfindOne
- find the first matching objectcount
- count the total number of matching objectsaggregate
- run a custom aggregator on a collection/array for a given field
- Supported operations
is
- equals matchisIgnoreCase
- equals match ignoring case on stringsisNull
- check if a property is nullisNotNull
- check if a property is not nulllike
- wildcard match on stringsregex
- Java regular expression match on stringshas
- check if value is contained in a collection or an arrayhasAll
- check if all values are contained in a collection or an arrayhasAny
- check if any of the value are present in a collection or an arraynot
- negate the match expressionexistsProperty
- check if property exists on objectnotExistsProperty
- check if property does not exists on objectlessThan
- if a value is less than property valuelessThanOrEquals
- if a value is equal or less than property valuegreaterThan
- if a value is greater than property valuegreaterThanOrEquals
- if a value is equal or greater than property value
- Boolean operations supported
and
- Boolean AND between two clausesor
- Boolean OR between two clauses
- Aggregation operations
averageAsLong
- find average value of a field which has no decimal partaverageAsDouble
- find average value of a field which has a decimal partminAsLong
- find minimum value of a field which has no decimal partminAsDouble
- find minimum value of a field which has a decimal partmaxAsLong
- find maximum value of a field which has no decimal partmaxAsDouble
- find maximum value of a field which has a decimal partsumAsLong
- find total sum of value of a field which has no decimal partsumAsDouble
- find total sum of value of a field which has a decimal partunique
- find the number of unique objects from the result setcount
- count objects in a collection/array which have a given field
RoadMap
- Support for
uniqueAs("someKey", String.class)
to returnSet<String>
thanSet<Object>
- Support for
groupBy("someKey")
to return aMap<Object, List<Object>>
Clause chaining and Evaluation
When more than one clause is added to the query and Boolean operations like AND
or OR
are used to connect them, the evaluation happens from left to right. The first clause is evaluated first and then the result is boolean AND
/OR
with the result of next clause depending on connecting operation type.
For example:
Gather.where("name").like("sandeep*").and("age").lessThan(50).or("status").is("active");
The evaluation happens in the order:
boolean first = evaluate("name like 'sandeep*'");
boolean second = evaluate("age < 50");
boolean third = evaluate("status == 'active'");
return first & second | third;
Performance
Performance numbers and percentage changes (compared to previous run). Values are operations-per-second. An operation is a query fired over a million objects created randomly for a LIKE
clause and a GREATER THAN
clause.
Version | Date Run | LIKE clause |
%age change | Numeric GREATER THAN clause |
%age change |
---|---|---|---|---|---|
1.0.0 | 10 Jan 2018 | 3.240 | n/a | 3.215 | n/a |
1.2.0 | 10 Jan 2018 | 4.160 | +28 | 5.792 | +80 |
Snapshot | 10 Jan 2018 | 5.322 | +28 | 5.735 | +0 |
Test Machine specs:
Macbook Pro (2017)
2.9GHz Intel i7
16GB RAM
OS X 10.12.6
Oracle JDK 1.8.0_131
Tests run from inside Eclipse Oxygen.2 (4.7.2) release
JMH options used were:
# JMH version: 1.19
# VM version: JDK 1.8.0_131, VM 25.131-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/bin/java
# VM options: -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Forks: 5
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
Caveat: Your mileage may vary.
Downloads
The latest stable release of the library can be downloaded via the Maven Central using the following coordinates:
<dependency>
<groupId>com.sangupta</groupId>
<artifactId>gather</artifactId>
<version>1.2.0</version>
</dependency>
The current/latest development snapshot JAR
can be obtained using JitPack.io
as:
Add the following repository to Maven,
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
Then add the dependency as,
<dependency>
<groupId>com.github.sangupta</groupId>
<artifactId>gather</artifactId>
<version>-SNAPSHOT</version>
</dependency>
Release Notes
Snapshot
- Improve performance of
wildcard
matches
1.2.0 (21 Dec 2017)
- Query on
object arrays
than justCollections
- Added caching to improve performance
- Updated for newer OSSRH release guidelines
- Updated
javadocs
and copyright headers
1.0.0 (09 Jun 2017)
- First stable release
Versioning
For transparency and insight into our release cycle, and for striving to maintain backward compatibility, gather
will be maintained under the Semantic Versioning guidelines as much as possible.
Releases will be numbered with the follow format:
<major>.<minor>.<patch>
And constructed with the following guidelines:
- Breaking backward compatibility bumps the major
- New additions without breaking backward compatibility bumps the minor
- Bug fixes and misc changes bump the patch
For more information on SemVer, please visit http://semver.org/.
License
gather: SQL queries for Java collections
Copyright (c) 2017-2018, Sandeep Gupta
https://sangupta.com/projects/gather
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.