- CQEngine attributes can now be created from Java 8 lambda expressions and method references:
Attribute<Car, Double> PRICE = attribute(Car::getPrice);
Attribute<Car, Boolean> IS_CHEAP = attribute(car -> car.getPrice() < 4000);
- See LambdaAttributes for more details.
- Improved support for Java 8 Streams:
- A
StreamFactory
class is now provided to convert CQEngine ResultSets to Java 8 Streams.- See Streams for more details.
- Note: despite these Java 8 features, CQEngine remains fully compatible with Java 6 & 7.
- A
- Support for DateMath queries (via DateMathParser), and support for SQL boolean literals has been added to the SQL query dialect (issue #88).
- Fixed issue where JOINs to a TransactionalIndexedCollection did not release read locks (issue #89).
- Maintenance release.
- Improved performance of
ResultSet.isEmpty()
/isNotEmpty()
when results are to be ordered (issue #78). - Added
HashIndex.onSemiUniqueAttribute()
as a way to reduce memory overhead of aHashIndex
(issue #67). - Fixed potential ClassCastException with combinations of certain queries with
CompoundIndex
(issue #85). - Improved
IndexedCollection.retainAll()
to avoid opening two connections to non-heap persistence and to reuse a single connection instead. - Improved
IndexedCollection.iterator().remove()
to integrate better with how resources are closed.
- CQEngine now supports Partial Indexes.
- The following new indexes are provided:
- Partial Indexes are similar to regular indexes and provide the same features, but they are also configured with an arbitrary filter query which restricts the objects which will be stored in the index to those which match the filter query.
- CQEngine will then use a partial index to accelerate a query, when it finds that a partial index on an attribute referenced in the query is available, and the set of objects which would match the query are logically a subset of objects which would match the filter query of the partial index. See PartialIndex for details.
- As such, a partial index accelerates queries on an "interesting subset" of the collection, without incurring the overhead of indexing the entire collection.
- Partial indexes require less storage space or memory than non-partial indexes, and they can yield better query performance as well, because they will contain fewer irrelevant entries not pertaining to the query.
- Partial indexes are also particularly useful when used with index-accelerated ordering. They can store results which match a given filter query in pre-sorted order of the given attribute, which means that requesting results for that query and ordered by that attribute at runtime, can be answered quickly by the partial index without requiring any post-filtering or post-sorting.
- A new type of persistence: WrappingPersistence
- WrappingPersistence can wrap any Java collection, in a CQEngine IndexedCollection without any copying of objects.
- This can be a convenient way to run queries or build indexes on existing collections.
- However some caveats related to concurrency support and the performance of the underlying collection apply, see WrappingPersistence for details.
- New support for
IndexedCollection<Map>
(IndexedCollections where dynamic Maps are used instead of POJOs)- QueryFactory.mapAttribute() can dynamically create an attribute which will read the value of an entry in these maps.
- QueryFactory.mapEntity() and QueryFactory.primaryKeyedMapEntity() and can wrap these maps in optimized maps which accelerate equality and hashCode calculations improving query performance beyond what regular maps would allow.
- Many thanks to Chris Kimpton for this contribution via pull request #68.
- CQEngine is now OSGi compatible
- Many thanks to Eduardo Fernández León for this contribution via pull request #72.
- The MVCC algorithm in
TransactionalIndexedCollection
has been improved to prevent race conditions on the write path (issue #69). - The internal interface which indexes implement has been updated slightly, to introduce an ObjectSet abstraction, which allows iterators and resources used by indexes to be released sooner during indexing operations, instead of as batches when indexing is complete.
- Maintenance release.
- This version is not radically different from 2.5.0; the minor version was bumped only due to the following minor API change.
- Fixed issue #53, which involved removing some methods in QueryFactory which allowed
and()
andor()
queries to be created programmatically with only a single child query.and()
andor()
queries can only be created with a minimum of two child queries. Previously, calling these methods and supplying a single child resulted in an IllegalStateException at runtime. This change prevents these errors at compile time instead.
- In some rare cases, applications might still wish to create
and()
andor()
queries from a Collection of child queries.- The method which allowed to create these queries from a Collection of child queries has also been removed, because it allowed that collection to be empty or to contain only one child query - which similarly would have resulted in an IllegalStateException.
- However applications which still wish to do this, can use the constructor of the And and Or query objects directly, instead of creating these queries via QueryFactory.
- Fixed issue #53, which involved removing some methods in QueryFactory which allowed
- Other changes in this release:
- Fixed issue #57: CQNParser now supports negative numbers.
- Fixed issue #49: The NodeFactory used by the following indexes can now be configured, which can be useful to tune memory usage:
RadixTreeIndex
,InvertedRadixTreeIndex
,ReversedRadixTreeIndex
,SuffixTreeIndex
. - Merged Pull Request #61
- This adds supports to reduce the latency of bulk imports into DiskIndex, or when populating large IndexedCollections which are persisted to disk, by suspending synchronous writes to disk and suspending journaling, while the bulk import is in progress. For details see
SQLiteIndexFlags.BULK_IMPORT_SUSPEND_SYNC_AND_JOURNALING
.
- This adds supports to reduce the latency of bulk imports into DiskIndex, or when populating large IndexedCollections which are persisted to disk, by suspending synchronous writes to disk and suspending journaling, while the bulk import is in progress. For details see
- Significant improvements in the write performance of disk and off-heap indexes, via IO batching
- Previously, when objects were added to the collection, they were added to each index on disk in a separate IO operation per index.
- Now, all operations are batched into a single round trip to disk, containing updates to all indexes which share the same type of persistence.
- Improved concurrency in DiskIndex
- Previously, the DiskIndex was unlike the on-heap indexes in that it did not fully support concurrent reads and writes: it supported concurrent reads, but writes were blocking.
- Now, the DiskIndex supports fully concurrent reads and writes, and reads are lock-free using MVCC.
- Improved locking support in OffHeapIndex
- The OffHeapIndex does not yet support full concurrency: it supports concurrent reads, but writes will block reads.
- This is a limitation in SQLite, on which OffHeapIndex depends.
- Nonetheless, the locking support in OffHeapIndex has been improved. Previously if this index was in use, a writing thread would receive an exception. Now writes will transparently wait for reads to finish.
- The OffHeapIndex does not yet support full concurrency: it supports concurrent reads, but writes will block reads.
- CompositePersistence and pluggable persistence implementations
- CQEngine now allows custom indexes using arbitrary Persistence implementations to be plugged in seamlessly.
- Different types of persistence may be combined using the CompositePersistence class.
- A new abstraction called ObjectStore allows the collection itself to be persisted on top of distributed caches, external databases, and still integrate with existing indexes.
- Improved query performance for off-heap and disk persistence
- Previously CQEngine would always use indexes to locate the smallest candidate set of objects, and it would then use on-the-fly filtering to reduce the candidate set to the final set of objects which match the query.
- This is retrospectively known as the "default merge strategy".
- This worked fine when the objects to be filtered were already on-heap, however it was expensive when the collection of objects itself was located off-heap or on disk, because candidate objects would need to be deserialized in order to perform this filtering.
- CQEngine can now answer the query without deserializing candidate objects, by performing joins between multiple indexes on-the-fly instead
- Only the final set of objects which actually match the query, will be deserialized; and lazily as the application requests them.
- This strategy can be requested by supplying query option EngineFlags.PREFER_INDEX_MERGE_STRATEGY.
- This requires that indexes are available for all of the attributes referenced in a query. If required indexes are not available, the default merge strategy will be used instead.
- Previously CQEngine would always use indexes to locate the smallest candidate set of objects, and it would then use on-the-fly filtering to reduce the candidate set to the final set of objects which match the query.
- Performance improvements for in() queries
- Previously in() queries were converted to potentially many equal() queries wrapped in an or() query.
- This meant that potentially many index lookups or filtering steps would be performed, to evaluate all of the values provided in the in() query.
- Now, all indexes support for in() queries directly, allowing these queries to be evaluated in fewer round trips.
- Backward compatibility
- The deduplication characteristics of ResultSet.size() has been changed, when ordering is requested but deduplication is not requested:
- Previously, if a query requested that the results were to be ordered, but it did not request that they were to be deduplicated as well: the ResultSet.iterator() would return ordered and deduplicated results AND the ResultSet.size() method would reflect the outcome of that deduplication in its calculation of size too.
- Now, if a query requested that the results are to be ordered, but it does not request that they are to be deduplicated as well: the ResultSet.iterator() will return ordered and deduplicated results as before BUT the ResultSet.size() method will not reflect the outcome of that deduplication in its calculation of size, and the size returned may count duplicate objects.
- This change makes the calculation of size() faster when the application wants results to be ordered, but doesn't care about deduplication per-se, or when the query would not produce duplicates anyway.
- The previous behaviour can be reinstated by requesting both ordering and deduplication.
- In general, applications which want deduplication should request it explicitly, and they should not rely on the fact that ordering results sometimes provides deduplication as well.
- The deduplication characteristics of ResultSet.size() has been changed, when ordering is requested but deduplication is not requested:
- Maintenance release.
- Further improvements in index-accelerated ordering strategy.
- ResultSet now implements java.io.Closeable, allowing use with Java 7 try-with-resources - closes issue #52.
- Deployed to Maven Central.
- Maintenance release.
- Merged pull request #54 from devinrsmith which fixes an issue where calling MaterializingResultSet.hasNext() repeatedly without calling next() advances the iterator when it should not.
- Fixed an edge case when index-accelerated ordering is enabled: if the primary attribute used for sorting was multi-valued, and some objects did not have any value for that attribute, then the objects which did not have a value for the attribute could be returned after the main results even if they did not fully match the query.
- Deployed to Maven Central.
- Maintenance release.
- Merged pull requests from gzsombor and kminder which improve the handling of in() queries - many thanks!
- Deployed to Maven Central.
- Support for running SQL queries on the collection.
- Support for running CQN (CQEngine Native) string-based queries on the collection (queries with the same syntax as programmatic queries, but in string form).
- Significant performance improvements for complex queries.
- Bulk import support for Off-heap and Disk persistence.
- More fine-grained control over the ordering of objects by attributes where some objects might not have values for the attribute: orderBy(missingFirst(attribute)) and orderBy(missingLast(attribute)).
- Nearly all indexes (On-heap, Off-heap and Disk) can now accelerate standing queries; StandingQueryIndex, which was on-heap only, is deprecated.
- The statistics APIs exposed by indexes, now provide additional statistics on the distribution of values in the index, and allow applications to traverse indexes directly (for advanced use cases).
- The performance of the "index" ordering strategy, useful for time time-series queries is improved.
- Deployed to Maven central.
- For more information and usage examples for this release see this post.
- Updated Xerial SQLite driver used by
DiskIndex
andOffHeapIndex
to 3.8.10.1, improves hot redeploy support in webapps - Deployed to Maven central
- Improved the performance of
DiskIndex
andOffHeapIndex
when processing stringstartsWith
queries - Deployed to Maven central
- Fixed exception when using index ordering strategy, when a range of values in the query is not in the collection
- Added validation to prevent duplicate indexes getting added
- Deployed to Maven central
- New
TransactionalIndexedCollection
supports read-committed transaction isolation using Multi-Version Concurrency Control (MVCC) - New ordering algorithm which can take advantage of indexes (see
orderingStrategy(INDEX)
) - New support to generate attributes automatically as bytecode (see
AttributeBytecodeGenerator
) IndexedCollection
can now be located in off-heap memory, or persisted on disk (seeOffHeapPersistence
andDiskPersistence
)- New indexes can be located in off-heap memory, or persisted on disk (see
OffHeapIndex
andDiskIndex
) - Tested with an IndexedCollections of 100 million objects
- CQEngine 2.0 contains some API changes since 1.3.2 - see the updated examples on the site for details
- Deployed to Maven central
- Bugfix to
NavigableIndex
's handling of range queries where the range values are exclusive and a quantizer is configured - Deployed to Maven central
- Added new query types:
all()
,none()
, andregexMatches()
- Deployed to Maven central
- Maintenance release
- Bugfix for
ObjectLockingIndexedCollection.StripedLock
's handling of negative hashcodes (issue 35) - Deployed to Maven central
- Maintenance release
- Bugfix for deduplication MATERIALIZE strategy (issue 32)
- Deployed to Maven central
- Maintenance release
- Fixed "FilteringIterator.hasNext advances iterator every time it is called", with thanks to Gabe Hicks for patch (issue 23)
- Fixed performance bottleneck in
Query.hashCode()
, with thanks to Atul Vasu for contribution (issue 25) - Fixed "Set remove() and removeAll() doesn't follow substitutability principle", with thanks to Atul Vasu for patch (issue 27)
- Deployed to Maven central
- Added
AttributesGenerator
, it is no longer necessary to write attributes by hand (issue 22) - See wiki page AttributesGenerator
- Deployed to Maven central
- Improved the toString representation of Query objects, query toStrings are now human readable (issue 19)
- Bugfix in
SelfAttribute
(issue 21) - Deployed to Maven central
- Added support to sort results in ascending/descending order on an attribute-by-attribute basis, with thanks to Roberto Socrates for patch (issue 16)
- Example usage:
cars.retrieve(query, queryOptions(orderBy(ascending(Car.DOORS), descending(Car.PRICE))))
- This release involves a minor API change from CQEngine 1.1.x, so is not a drop-in replacement
- Code using the old API to order results such as
cars.retrieve(query, queryOptions(orderByDescending(Car.PRICE, Car.DOORS)))
should be changed to that above - Deployed to Maven central
- Additional work on
ReflectiveAttribute
to play nicer with inherited fields (issue 18) - Deployed to Maven central
- Updated
RadixTreeIndex
,ReversedRadixTreeIndex
,InvertedRadixTreeIndex
,SuffixTreeIndex
to use concurrent-trees library 2.1 for lower memory usage and lower latency (issue 14) - Added
CQEngine.newObjectLockingInstance(int concurrencyLevel)
providing object-level locking on the write path for applications in which threads might race each other to add/remove the same object (issue 9) - Bugfix to hashCode implementation in Not.java, would reduce collisions if queries stored in hash maps (issue 13)
- Added support to
SimpleAttribute
to specify generic types manually via constructor to bypass reflection (issue 17) - Bugfix to
ReflectiveAttribute
to allow it to access private fields (issue 18) - Deployed to Maven central
- Added new type of index,
UniqueIndex
, with thanks to Kinz Liu (issue 8) - can be used with primary-key type attributes to reduce memory usage and yield faster query performance - Added
ReflectiveAttribute
- reflection-based attribute, for trading performance for convenience/flexibility/simplifying dynamic queries in some cases (issue 6) - Added benchmarks for indexing overhead (issue 7)
- Deployed to Maven central
- Added support for Has queries -
has(Car.DESCRIPTION)
andnot(has(Car.DESCRIPTION))
- the equivalent of SQLIS NOT NULL
andIS NULL
respectively. Use withSimpleNullableAttribute
. These queries can be accelerated viaStandingQueryIndex
- Bugfix to updating compound indexes for objects added/removed after index added
- Deployed to Maven central
- Added support for customizing via factories, the construction of maps and sets used internally by
HashIndex
,NavigableIndex
andCompoundIndex
(concurrency level, load factor etc.) (issue 5) - Deployed to Maven central
- Added support for attributes returning null values via
SimpleNullableAttribute
,MultiValueNullableAttribute
(issue 2) - Bugfix to
ResultSet.uniqueResult()
(issue 4) - API same as in 0.9.1, no code changes necessary
- Deployed to Maven central
- Deployed to Maven central
- Public upload of source