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
- Usecases
- Usage Examples
- Composed Objects and Keys
- Features
- Roadmap
- Clause chaining and Evaluation
- Performance
- Downloads
- Release Notes
- Versioning
- License
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
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);
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;
}
- 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
- Support for
uniqueAs("someKey", String.class)
to returnSet<String>
thanSet<Object>
- Support for
groupBy("someKey")
to return aMap<Object, List<Object>>
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 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.
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>
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
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/.
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.