{"id":13485941,"url":"https://github.com/npgall/cqengine","last_synced_at":"2025-05-14T03:08:55.077Z","repository":{"id":37431842,"uuid":"41263775","full_name":"npgall/cqengine","owner":"npgall","description":"Ultra-fast SQL-like queries on Java collections","archived":false,"fork":false,"pushed_at":"2023-12-27T20:30:52.000Z","size":11503,"stargazers_count":1748,"open_issues_count":78,"forks_count":253,"subscribers_count":86,"default_branch":"master","last_synced_at":"2025-04-10T20:54:50.546Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/npgall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-23T19:27:00.000Z","updated_at":"2025-04-10T11:38:31.000Z","dependencies_parsed_at":"2024-01-03T01:21:41.972Z","dependency_job_id":"8b652895-6192-479d-97ed-3191874e5949","html_url":"https://github.com/npgall/cqengine","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npgall%2Fcqengine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npgall%2Fcqengine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npgall%2Fcqengine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/npgall%2Fcqengine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/npgall","download_url":"https://codeload.github.com/npgall/cqengine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254059508,"owners_count":22007768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T18:00:34.636Z","updated_at":"2025-05-14T03:08:55.057Z","avatar_url":"https://github.com/npgall.png","language":"Java","readme":"[![Build Status](https://travis-ci.org/npgall/cqengine.svg?branch=master)](https://travis-ci.org/npgall/cqengine)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.googlecode.cqengine/cqengine/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.googlecode.cqengine%22%20AND%20a%3Acqengine)\n\n\n\n# CQEngine - Collection Query Engine #\n\nCQEngine – Collection Query Engine – is a high-performance Java collection which can be searched with SQL-like queries, with _extremely_ low latency.\n\n  * Achieve millions of queries per second, with query latencies measured in microseconds\n  * Offload query traffic from databases - scale your application tier\n  * Outperform databases by a factor of thousands, even on low-end hardware\n\nSupports on-heap persistence, off-heap persistence, disk persistence, and supports MVCC transaction isolation.\n\nInteresting reviews of CQEngine:\n  * [dzone.com: Comparing the search performance of CQEngine with standard Java collections](https://dzone.com/articles/comparing-search-performance)\n  * [dzone.com: Getting started with CQEngine: LINQ for Java, only faster](https://dzone.com/articles/getting-started-cqengine-linq)\n  * CQEngine in the wild: [excelian.com](http://www.excelian.com/exposure-and-counterparty-limit-checking) | [gravity4.com](http://gravity4.com/welcome-gravity4-engineering-blog/) | [snapdeal.com](http://engineering.snapdeal.com/how-were-building-a-system-to-scale-for-billions-of-requests-per-day-201601/) (3-5 billion requests/day)\n\n## The Limits of Iteration ##\nThe classic way to retrieve objects matching some criteria from a collection, is to iterate through the collection and apply some tests to each object. If the object matches the criteria, then it is added to a result set. This is repeated for every object in the collection.\n\nConventional iteration is hugely inefficient, with time complexity O(_n_ _t_). It can be optimized, but requires **statistical knowledge** of the makeup of the collection. [Read more: The Limits of Iteration](documentation/TheLimitsOfIteration.md)\n\n**Benchmark Sneak Peek**\n\nEven with optimizations applied to convention iteration, CQEngine can outperform conventional iteration by wide margins. Here is a graph for a test comparing CQEngine latency with iteration for a range-type query:\n\n![quantized-navigable-index-carid-between.png](documentation/images/quantized-navigable-index-carid-between.png)\n\n  * **1,116,071 queries per second** (on a single 1.8GHz CPU core)\n  * **0.896 microseconds per query**\n  * CQEngine is **330187.50% faster** than naive iteration\n  * CQEngine is **325727.79% faster** than optimized iteration\n\nSee the [Benchmark](documentation/Benchmark.md) wiki page for details of this test, and other tests with various types of query.\n\n\n---\n\n\n## CQEngine Overview ##\n\nCQEngine solves the scalability and latency problems of iteration by making it possible to build _indexes_ on the fields of the objects stored in a collection, and applying algorithms based on the rules of set theory to _reduce the time complexity_ of accessing them.\n\n**Indexing and Query Plan Optimization**\n\n  * **Simple Indexes** can be added to any number of individual fields in a collection of objects, allowing queries on just those fields to be answered in O(_1_) time complexity\n  * **Multiple indexes on the same field** can be added, each optimized for different types of query - for example equality, numerical range, string starts with etc.\n  * **Compound Indexes** can be added which span multiple fields, allowing queries referencing several fields to also be answered in O(_1_) time complexity\n  * **Nested Queries** are fully supported, such as the SQL equivalent of \"`WHERE color = 'blue' AND(NOT(doors = 2 OR price \u003e 53.00))`\"\n  * **Standing Query Indexes** can be added; these allow _arbitrarily complex queries_, or _nested query fragments_, to be answered in O(_1_) time complexity, regardless of the number of fields referenced. Large queries containing branches or query fragments for which standing query indexes exist, will automatically benefit from O(_1_) time complexity evaluation of their branches; in total several indexes might be used to accelerate complex queries\n  * **Statistical Query Plan Optimization** - when several fields have suitable indexes, CQEngine will use statistical information from the indexes, to internally make a query plan which selects the indexes which can perform the query with minimum time complexity. When some referenced fields have suitable indexes and some do not, CQEngine will use the available indexes first, and will then iterate the smallest possible set of results from those indexes to filter objects for the rest of the query. In those cases time complexity will be greater than O(_1_), but usually significantly less than O(_n_)\n  * **Iteration fallback** -  if no suitable indexes are available, CQEngine will evaluate the query via iteration, using lazy evaluation. CQEngine can always evaluate every query, even if no suitable indexes are available. Queries are not coupled with indexes, so indexes can be added after the fact, to speed up existing queries\n  * **CQEngine supports full concurrency** and expects that objects will be added to and removed from the collection at runtime; CQEngine will take care of updating all registered indexes in realtime\n  * **Type-safe** - nearly all errors in queries result in _compile-time_ errors instead of exceptions at runtime: all indexes, and all queries, are strongly typed using generics at both object-level and field-level\n  * **On-heap/off-heap/disk** - objects can be stored on-heap (like a conventional Java collection), or off-heap (in native memory, within the JVM process but outside the Java heap), or persisted to disk\n\nSeveral implementations of CQEngine's `IndexedCollection` are provided, supporting various concurrency and transaction isolation levels:\n\n  * [ConcurrentIndexedCollection](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/ConcurrentIndexedCollection.html) - lock-free concurrent reads and writes with no transaction isolation\n  * [ObjectLockingIndexedCollection](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/ObjectLockingIndexedCollection.html) - lock-free concurrent reads, and some locking of writes for object-level transaction isolation and consistency guarantees\n  * [TransactionalIndexedCollection](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/TransactionalIndexedCollection.html)  - lock-free concurrent reads, and sequential writes for full [transaction isolation](documentation/TransactionIsolation.md) using Multi-Version Concurrency Control\n\nFor more details see [TransactionIsolation](documentation/TransactionIsolation.md).\n\n---\n\n## Complete Example ##\n\nIn CQEngine applications mostly interact with [IndexedCollection](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/IndexedCollection.html), which is an implementation of [java.util.Set](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html), and it provides two additional methods:\n\n  * [addIndex(SomeIndex)](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/engine/QueryEngine.html#addIndex(com.googlecode.cqengine.index.Index)) allows indexes to be added to the collection\n  * [retrieve(Query)](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/engine/QueryEngine.html#retrieve(com.googlecode.cqengine.query.Query)) accepts a [Query](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/query/Query.html) and returns a [ResultSet](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html) providing objects matching that query. `ResultSet` implements [java.lang.Iterable](http://docs.oracle.com/javase/6/docs/api/java/lang/Iterable.html), so accessing results is achieved by iterating the result set, or accessing it as a Java 8+ Stream\n\nHere is a **complete example** of how to build a collection, add indexes and perform queries. It does not discuss _attributes_, which are discussed below.\n\n**STEP 1: Create a new indexed collection**\n\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e();\n```\n\n**STEP 2: Add some indexes to the collection**\n\n```java\ncars.addIndex(NavigableIndex.onAttribute(Car.CAR_ID));\ncars.addIndex(ReversedRadixTreeIndex.onAttribute(Car.NAME));\ncars.addIndex(SuffixTreeIndex.onAttribute(Car.DESCRIPTION));\ncars.addIndex(HashIndex.onAttribute(Car.FEATURES));\n```\n\n**STEP 3: Add some objects to the collection**\n\n```java\ncars.add(new Car(1, \"ford focus\", \"great condition, low mileage\", Arrays.asList(\"spare tyre\", \"sunroof\")));\ncars.add(new Car(2, \"ford taurus\", \"dirty and unreliable, flat tyre\", Arrays.asList(\"spare tyre\", \"radio\")));\ncars.add(new Car(3, \"honda civic\", \"has a flat tyre and high mileage\", Arrays.asList(\"radio\")));\n```\n\n\n**STEP 4: Run some queries**\n\nNote: add import statement to your class: _`import static com.googlecode.cqengine.query.QueryFactory.*`_\n\n* *Example 1: Find cars whose name ends with 'vic' or whose id is less than 2*\n\n  Query:\n  ```java\n    Query\u003cCar\u003e query1 = or(endsWith(Car.NAME, \"vic\"), lessThan(Car.CAR_ID, 2));\n    cars.retrieve(query1).forEach(System.out::println);\n  ```\n  Prints:\n  ```\n    Car{carId=3, name='honda civic', description='has a flat tyre and high mileage', features=[radio]}\n    Car{carId=1, name='ford focus', description='great condition, low mileage', features=[spare tyre, sunroof]}\n  ```\n  \n* *Example 2: Find cars whose flat tyre can be replaced*\n\n  Query:\n  ```java\n    Query\u003cCar\u003e query2 = and(contains(Car.DESCRIPTION, \"flat tyre\"), equal(Car.FEATURES, \"spare tyre\"));\n    cars.retrieve(query2).forEach(System.out::println);\n  ```\n  Prints:\n  ```\n    Car{carId=2, name='ford taurus', description='dirty and unreliable, flat tyre', features=[spare tyre, radio]}\n  ```\n  \n* *Example 3: Find cars which have a sunroof or a radio but are not dirty*\n\n  Query:\n  ```java\n    Query\u003cCar\u003e query3 = and(in(Car.FEATURES, \"sunroof\", \"radio\"), not(contains(Car.DESCRIPTION, \"dirty\")));\n    cars.retrieve(query3).forEach(System.out::println);\n  ```\n   Prints:\n  ```\n    Car{carId=1, name='ford focus', description='great condition, low mileage', features=[spare tyre, sunroof]}\n    Car{carId=3, name='honda civic', description='has a flat tyre and high mileage', features=[radio]}\n  ```\n\nComplete source code for these examples can be found [here](http://github.com/npgall/cqengine/blob/master/code/src/test/java/com/googlecode/cqengine/examples/introduction/).\n\n---\n\n## String-based queries: SQL and CQN dialects ##\n\nAs an alternative to programmatic queries, CQEngine also has support for running string-based queries on the collection, in either SQL or CQN (CQEngine Native) format.\n\nExample of running an SQL query on a collection (full source [here](https://github.com/npgall/cqengine/blob/master/code/src/test/java/com/googlecode/cqengine/examples/parser/SQLQueryDemo.java)):\n```java\npublic static void main(String[] args) {\n    SQLParser\u003cCar\u003e parser = SQLParser.forPojoWithAttributes(Car.class, createAttributes(Car.class));\n    IndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e();\n    cars.addAll(CarFactory.createCollectionOfCars(10));\n\n    ResultSet\u003cCar\u003e results = parser.retrieve(cars, \"SELECT * FROM cars WHERE (\" +\n                                    \"(manufacturer = 'Ford' OR manufacturer = 'Honda') \" +\n                                    \"AND price \u003c= 5000.0 \" +\n                                    \"AND color NOT IN ('GREEN', 'WHITE')) \" +\n                                    \"ORDER BY manufacturer DESC, price ASC\");\n                                    \n    results.forEach(System.out::println); // Prints: Honda Accord, Ford Fusion, Ford Focus\n}\n```\n\nExample of running a CQN query on a collection (full source [here](https://github.com/npgall/cqengine/blob/master/code/src/test/java/com/googlecode/cqengine/examples/parser/CQNQueryDemo.java)):\n```java\npublic static void main(String[] args) {\n    CQNParser\u003cCar\u003e parser = CQNParser.forPojoWithAttributes(Car.class, createAttributes(Car.class));\n    IndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e();\n    cars.addAll(CarFactory.createCollectionOfCars(10));\n\n    ResultSet\u003cCar\u003e results = parser.retrieve(cars,\n                                    \"and(\" +\n                                        \"or(equal(\\\"manufacturer\\\", \\\"Ford\\\"), equal(\\\"manufacturer\\\", \\\"Honda\\\")), \" +\n                                        \"lessThanOrEqualTo(\\\"price\\\", 5000.0), \" +\n                                        \"not(in(\\\"color\\\", GREEN, WHITE))\" +\n                                    \")\");\n                                    \n    results.forEach(System.out::println); // Prints: Ford Focus, Ford Fusion, Honda Accord\n}\n```\n\n---\n\n## Feature Matrix for Included Indexes ##\n\n**Legend for the feature matrix**\n\n| **Abbreviation** | **Meaning** | **Example** |\n|:-----------------|:------------|:------------|\n| **EQ**           | _Equality_  | `equal(Car.DOORS, 4)` |\n| **IN**           | _Equality, multiple values_ | `in(Car.DOORS, 3, 4, 5)` |\n| **LT**           | _Less Than (numerical range / `Comparable`)_ | `lessThan(Car.PRICE, 5000.0)` |\n| **GT**           | _Greater Than (numerical range / `Comparable`)_ | `greaterThan(Car.PRICE, 2000.0)` |\n| **BT**           | _Between (numerical range / `Comparable`)_ | `between(Car.PRICE, 2000.0, 5000.0)` |\n| **SW**           | _String Starts With_ | `startsWith(Car.NAME, \"For\")` |\n| **EW**           | _String Ends With_ | `endsWith(Car.NAME, \"ord\")` |\n| **SC**           | _String Contains_ | `contains(Car.NAME, \"or\")` |\n| **CI**           | _String Is Contained In_ | `isContainedIn(Car.NAME, \"I am shopping for a Ford Focus car\")` |\n| **RX**           | _String Matches Regular Expression_ | `matchesRegex(Car.MODEL, \"Ford.*\")` |\n| **HS**           | _Has (aka `IS NOT NULL`)_ | `has(Car.DESCRIPTION)` / `not(has(Car.DESCRIPTION))` |\n| **SQ**           | _Standing Query_ | _Can the index accelerate a query (as opposed to an attribute) to provide constant time complexity for any simple query, complex query, or fragment_ |\n| **QZ**           | _Quantization_ | _Does the index accept a quantizer to control granularity_ |\n| **LP**           | _LongestPrefix_ | `longestPrefix(Car.NAME, \"Ford\")` |\n\nNote: CQEngine also supports complex queries via **`and`**, **`or`**, **`not`**, and combinations thereof, across all indexes.\n\n**Index Feature Matrix**\n\n| \u003csub\u003e**Index Type**\u003c/sub\u003e | \u003csub\u003e**EQ**\u003c/sub\u003e | \u003csub\u003e**IN**\u003c/sub\u003e | \u003csub\u003e**LT**\u003c/sub\u003e | \u003csub\u003e**GT**\u003c/sub\u003e | \u003csub\u003e**BT**\u003c/sub\u003e | \u003csub\u003e**SW**\u003c/sub\u003e | \u003csub\u003e**EW**\u003c/sub\u003e | \u003csub\u003e**SC**\u003c/sub\u003e | \u003csub\u003e**CI**\u003c/sub\u003e | \u003csub\u003e**HS**\u003c/sub\u003e | \u003csub\u003e**RX**\u003c/sub\u003e | \u003csub\u003e**SQ**\u003c/sub\u003e | \u003csub\u003e**QZ**\u003c/sub\u003e | \u003csub\u003e**LP**\u003c/sub\u003e |\n|:---------------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|:-------|\n| [\u003csub\u003eHash\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/hash/HashIndex.html) | ✓      | ✓      |        |        |        |        |        |        |        |        |        |        | ✓      |        |\n| [\u003csub\u003eUnique\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/unique/UniqueIndex.html) | ✓      | ✓      |        |        |        |        |        |        |        |        |        |        |        |        |\n| [\u003csub\u003eCompound\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/compound/CompoundIndex.html) | ✓      | ✓      |        |        |        |        |        |        |        |        |        |        | ✓      |        |\n| [\u003csub\u003eNavigable\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/navigable/NavigableIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        |        |        | ✓      |        |\n| [\u003csub\u003ePartialNavigable\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/navigable/PartialNavigableIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        |        | ✓      |        |        |\n| [\u003csub\u003eRadixTree\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/radix/RadixTreeIndex.html) | ✓      | ✓      |        |        |        | ✓      |        |        |        |        |        |        |        |        |\n| [\u003csub\u003eReversedRadixTree\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/radixreversed/ReversedRadixTreeIndex.html) | ✓      | ✓      |        |        |        |        | ✓      |        |        |        |        |        |        |        |\n| [\u003csub\u003eInvertedRadixTree\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/radixinverted/InvertedRadixTreeIndex.html) | ✓      | ✓      |        |        |        |        |        |        | ✓      |        |        |        |        | ✓      |\n| [\u003csub\u003eSuffixTree\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/suffix/SuffixTreeIndex.html) | ✓      | ✓      |        |        |        |        | ✓      | ✓      |        |        |        |        |        |        |\n| [\u003csub\u003eStandingQuery\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/standingquery/StandingQueryIndex.html) |        |        |        |        |        |        |        |        |        |        |        | ✓      |        |        |\n| [\u003csub\u003eFallback\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/fallback/FallbackIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      |        |        | ✓      |\n| [\u003csub\u003eOffHeap\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/offheap/OffHeapIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        | ✓\u003csup\u003e[1]\u003c/sup\u003e      |        |        |\n| [\u003csub\u003ePartialOffHeap\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/offheap/PartialOffHeapIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        | ✓      |        |        |\n| [\u003csub\u003eDisk\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/disk/DiskIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        | ✓\u003csup\u003e[1]\u003c/sup\u003e      |        |        |\n| [\u003csub\u003ePartialDisk\u003c/sub\u003e](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/index/disk/PartialDiskIndex.html) | ✓      | ✓      | ✓      | ✓      | ✓      | ✓      |        |        |        |        |        | ✓      |        |        |\n\u003csup\u003e[1]\u003c/sup\u003e See: [forStandingQuery()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/query/QueryFactory.html#forStandingQuery-com.googlecode.cqengine.query.Query-)\n\nThe [Benchmark](documentation/Benchmark.md) page contains examples of how to add these indexes to a collection, and measures their impact on latency.\n\n---\n\n## Attributes ##\n\n### Read Fields ###\n\nCQEngine needs to access fields inside objects, so that it can build indexes on fields, and retrieve the value of a certain field from any given object.\n\nCQEngine does not use reflection to do this; instead it uses **attributes**, which is a more powerful concept. An attribute is an accessor object which can read the value of a certain field in a POJO.\n\nHere's how to define an attribute for a Car object (a POJO), which reads the `Car.carId` field:\n```java\npublic static final Attribute\u003cCar, Integer\u003e CAR_ID = new SimpleAttribute\u003cCar, Integer\u003e(\"carId\") {\n    public Integer getValue(Car car, QueryOptions queryOptions) { return car.carId; }\n};\n```\n...or alternatively, from a lambda expression or method reference:\n```java\npublic static final Attribute\u003cCar, Integer\u003e Car_ID = attribute(\"carId\", Car::getCarId);\n```\n(For some caveats on using lambdas, please read [LambdaAttributes](documentation/LambdaAttributes.md))\n\nUsually attributes are defined as anonymous `static` `final` objects like this. Supplying the `\"carId\"` string parameter to the constructor is actually optional, but it is recommended as it will appear in query `toString`s.\n\nSince this attribute reads a field from a `Car` object, the usual place to put the attribute is inside the `Car` class - and this makes queries more readable. However it could really be defined in any class, such as in a `CarAttributes` class or similar. The example above is for a **[SimpleAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/SimpleAttribute.html)**, which is designed for fields containing only one value.\n\nCQEngine also supports **[MultiValueAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/MultiValueAttribute.html)** which can read the values of fields which themselves are collections. And so it supports building indexes on objects based on things like keywords associated with those objects.\n\nHere's how to define a `MultiValueAttribute` for a `Car` object which reads the values from `Car.features` where that field is a `List\u003cString\u003e`:\n```java\npublic static final Attribute\u003cCar, String\u003e FEATURES = new MultiValueAttribute\u003cCar, String\u003e(\"features\") {\n    public Iterable\u003cString\u003e getValues(Car car, QueryOptions queryOptions) { return car.features; }\n};\n```\n...or alternatively, from a lambda expression or method reference:\n```java\npublic static final Attribute\u003cCar, String\u003e FEATURES = attribute(String.class, \"features\", Car::getFeatures);\n```\n\n#### Null values ####\nNote **if your data contains `null` values**, you should use **[SimpleNullableAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/SimpleNullableAttribute.html)** or **[MultiValueNullableAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/MultiValueNullableAttribute.html)** instead.\n\nIn particular, note that `SimpleAttribute` and `MultiValueAttribute` do not perform any null checking on your data, and so if your data inadvertently contains null values, you may get obscure `NullPointerException`s. This is because null checking does not come for free. Attributes are accessed heavily, and the non-nullable versions of these attributes are designed to minimize latency by skipping explicit null checks. They defer to the JVM to do the null checking implicitly. \n\nAs a rule of thumb, if you get a `NullPointerException`, it's probably because you used the wrong type of attribute. The problem will usually go away if you switch your code to use a nullable attribute instead. If you don't know if your data may contain null values, just use the nullable attributes. They contain the logic to check for and handle null values automatically.\n\nThe nullable attributes also allow CQEngine to work with [object inheritance](https://github.com/npgall/cqengine/tree/master/code/src/test/java/com/googlecode/cqengine/examples/inheritance), where some objects in the collection have certain optional fields (e.g. in subclasses) while others might not.\n\n#### Creating queries dynamically ####\n\nDynamic queries can be composed at runtime by instantiating and combining Query objects directly; see [this package](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/query/simple/package-summary.html) and [this package](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/query/logical/package-summary.html). For advanced cases, it is also possible to define attributes at runtime, using [ReflectiveAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/ReflectiveAttribute.html) or [AttributeBytecodeGenerator](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/codegen/AttributeBytecodeGenerator.html).\n\n#### Generate attributes automatically ####\n\nCQEngine also provides several ways to generate attributes automatically.\n\nNote these are an alternative to using [ReflectiveAttribute](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/attribute/ReflectiveAttribute.html), which was discussed above. Whereas `ReflectiveAttribute` is a special type of attribute which reads values at runtime using reflection, `AttributeSourceGenerator` and `AttributeBytecodeGenerator` generate code for attributes which is compiled and so does not use reflection at runtime, which can be more efficient.\n\n  * [AttributeSourceGenerator](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/codegen/AttributeSourceGenerator.html) can automatically generate the source code for the simple and multi-value attributes discussed above.\n  * [AttributeBytecodeGenerator](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/codegen/AttributeBytecodeGenerator.html) can automatically generate the class bytecode for the simple and multi-value attributes discussed above, and load them into the application at runtime as if they had been compiled from source code.\n\nSee [AutoGenerateAttributes](documentation/AutoGenerateAttributes.md) for more details.\n\n### Attributes as Functions ###\n\nIt can be noted that attributes are only required to return a value given an object. Although most will do so, there is no requirement that an attribute must provide a value by reading a field in the object. As such attributes can be _virtual_, implemented as _functions_.\n\n**Calculated Attributes**\n\nAn attribute can **_calculate_** an appropriate value for an object, based on a function applied to data contained in other fields or from external data sources.\n\nHere's how to define a calculated (or virtual) attribute by applying a function over the Car's other fields:\n```java\npublic static final Attribute\u003cCar, Boolean\u003e IS_DIRTY = new SimpleAttribute\u003cCar, Boolean\u003e(\"is_dirty\") {\n    public Boolean getValue(Car car, QueryOptions queryOptions) { return car.description.contains(\"dirty\"); }\n};\n```\n...or, the same thing using a lambda:\n```java\npublic static final Attribute\u003cCar, Boolean\u003e IS_DIRTY = attribute(\"is_dirty\", car -\u003e car.description.contains(\"dirty\"));\n```\n\nA `HashIndex` could be built on the virtual attribute above, enabling fast retrievals of cars which are either dirty or not dirty, without needing to scan the collection.\n\n**Associations with other `IndexedCollections` or External Data Sources**\n\nHere is an example for a virtual attribute which **associates** with each `Car` a list of locations which can service it, from an external data source:\n```java\npublic static final Attribute\u003cCar, String\u003e SERVICE_LOCATIONS = new MultiValueAttribute\u003cCar, String\u003e() {\n    public List\u003cString\u003e getValues(Car car, QueryOptions queryOptions) {\n        return CarServiceManager.getServiceLocationsForCar(car);\n    }\n};\n```\nThe attribute above would allow the `IndexedCollection` of cars to be searched for cars which have _servicing options in a particular location_.\n\nThe locations which service a car, could alternatively be retrieved from another `IndexedCollection`, of `Garage`s, for example. **Care should be taken if building indexes on virtual attributes** however, if referenced data might change leaving obsolete information in indexes. A **strategy to accommodate this** is: if no index exists for a virtual attribute referenced in a query, and other attributes are also referenced in the query for which indexes exist, CQEngine will automatically reduce the candidate set of objects to the minimum using other indexes before querying the virtual attribute. In turn if virtual attributes perform retrievals from _other_ `IndexedCollection`s, then those collections could be indexed appropriately without a risk of stale data.\n\n---\n\n### Joins ###\n\nThe examples above define attributes on a primary `IndexedCollection` which read data from secondary collections or external data sources.\n\nIt is also possible to perform SQL EXISTS-type queries and JOINs between `IndexedCollection`s on the query side (as opposed to on the attribute side). See [Joins](documentation/Joins.md) for examples.\n\n\n---\n\n\n## Persistence on-heap, off-heap, disk ##\n\nCQEngine's `IndexedCollection`s can be configured to store objects added to them on-heap (the default), or off-heap, or on disk.\n\n**On-heap**\n\nStore the collection on the Java heap:\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e();\n```\n\n**Off-heap**\n\nStore the collection in native memory, within the JVM process but outside the Java heap:\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e(OffHeapPersistence.onPrimaryKey(Car.CAR_ID));\n```\n\nNote that the off-heap persistence will automatically create an index on the specified primary key attribute, so there is no need to add an index on that attribute later.\n\n**Disk**\n\nStore the collection in a temp file on disk (then see `DiskPersistence.getFile()`):\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e(DiskPersistence.onPrimaryKey(Car.CAR_ID));\n```\nOr, store the collection in a particular file on disk:\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e(DiskPersistence.onPrimaryKeyInFile(Car.CAR_ID, new File(\"cars.dat\")));\n```\n\nNote that the disk persistence will automatically create an index on the specified primary key attribute, so there is no need to add an index on that attribute later.\n\n**Wrapping**\n\nWrap any Java collection, in a CQEngine IndexedCollection without any copying of objects.\n * This can be a convenient way to run queries or build indexes on existing collections.\n * However some caveats relating to concurrency support and the performance of the underlying collection apply, see [WrappingPersistence](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/persistence/wrapping/WrappingPersistence.html) for details.\n\n```java\nCollection\u003cCar\u003e collection = // obtain any Java collection\n\nIndexedCollection\u003cCar\u003e indexedCollection = new ConcurrentIndexedCollection\u003cCar\u003e(\n        WrappingPersistence.aroundCollection(collection)\n);\n```\n\n**Composite**\n\n`CompositePersistence` configures a combination of persistence types for use within the same collection.\nThe collection itself will be persisted in the first persistence provided (the _primary persistence_), and the additional persistences provided will be used by off-heap or disk indexes added to the collection subsequently.\n\nStore the collection on-heap, and also configure DiskPersistence for use by DiskIndexes added to the collection subsequently:\n```java\nIndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003cCar\u003e(CompositePersistence.of(\n    OnHeapPersistence.onPrimaryKey(Car.CAR_ID),\n    DiskPersistence.onPrimaryKeyInFile(Car.CAR_ID, new File(\"cars.dat\"))\n));\n```\n\n### Index persistence ###\n\nIndexes can similarly be stored on-heap, off-heap, or on disk. Each index requires a certain type of persistence. It is necessary to configure the collection in advance with an appropriate combination of persistences for use by whichever indexes are added.\n\nIt is possible to store the collection on-heap, but to store some indexes off-heap. Similarly it is possible to have a variety of index types on the same collection, each using a different type of persistence. On-heap persistence is by far the fastest, followed by off-heap persistence, and then by disk persistence.\n\nIf both the collection and all of its indexes are stored off-heap or on disk, then it is possible to have extremely large collections which don't use any heap memory or RAM at all.\n\nCQEngine has been tested using off-heap persistence with collections of 10 million objects, and using disk persistence with collections of 100 million objects.\n\n**On-heap**\n\nAdd an on-heap index on \"manufacturer\":\n```java\ncars.addIndex(NavigableIndex.onAttribute(Car.MANUFACTURER));\n```\n\n**Off-heap**\n\nAdd an off-heap index on \"manufacturer\":\n```java\ncars.addIndex(OffHeapIndex.onAttribute(Car.MANUFACTURER));\n```\n\n**Disk**\n\nAdd a disk index on \"manufacturer\":\n```java\ncars.addIndex(DiskIndex.onAttribute(Car.MANUFACTURER));\n```\n\n### Querying with persistence ###\n\nWhen either the `IndexedCollection`, or one or more indexes are located off-heap or on disk, take care to close the ResultSet when finished reading. You can use a _try-with-resources_ block to achieve this:\n```java\ntry (ResultSet\u003cCar\u003e results = cars.retrieve(equal(Car.MANUFACTURER, \"Ford\"))) {\n    results.forEach(System.out::println);\n}\n```\n---\n\n## Result Sets ##\n\nCQEngine [ResultSet](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html)s provide the following methods:\n\n  * [iterator()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#iterator()) - Allows the `ResultSet` to be iterated, returning the next object matching the query in each iteration as determined via _lazy evaluation_\n    * Result sets support **concurrent iteration** while the collection is being modified; the set of objects returned simply may or may not reflect changes made during iteration (depending on whether changes are made to areas of the collection or indexes already iterated or not)\n\n  * [uniqueResult()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#uniqueResult()) - Useful if the query is expected to only match one object, this method returns the first object which would be returned by the iterator, and it throws an exception if zero or more than one object is found\n\n  * [size()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#size()) - Returns the number of objects which _would be returned by the `ResultSet` if it was iterated_; CQEngine can often **accelerate** this calculation of size, based on the sizes of individual sets in indexes; see JavaDoc for details\n\n  * [contains()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#contains(O)) -  Tests if a _given object_ would be contained in results matching a query; this is also an **accelerated** operation; when suitable indexes are available, CQEngine can avoid iterating results to test for containment; see JavaDoc for details\n\n  * [getRetrievalCost()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#getRetrievalCost()) - This is a metric used internally by CQEngine to allow it to _choose between multiple indexes_ which support the query. This could occasionally be used by applications to ascertain if suitable indexes are available for any particular query, this will be `Integer.MAX_VALUE` for queries for which no suitable indexes are available\n\n  * [getMergeCost()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#getMergeCost()) - This is a metric used internally by CQEngine to allow it to _re-order_ elements of the query to minimize time complexity; for example CQEngine will order intersections such that the smallest set drives the _merge_; this metric is _roughly_ based on the theoretical cost to iterate underlying result sets\n    * For query fragments requiring _set union_ (`or`-based queries), this will be the _sum_ of merge costs from underlying result sets\n    * For query fragments requiring _set intersection_ (`and`-based queries), this will be the _Math.min()_ of merge costs from underlying result sets, because intersections will be re-ordered to perform lowest-merge-cost intersections first\n    * For query fragments requiring _set difference_ (`not`-based queries), this will be the merge cost from the first underlying result set\n\n  * [stream()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#stream()) - Returns a Java 8+ `Stream` allowing CQEngine results to be grouped, aggregated, and transformed in flexible ways using lambda expressions.\n \n  * [close()](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/resultset/ResultSet.html#close()) -  Releases any resources or closes the transaction which was opened for the query. Whether or not it is necessary to close the ResultSet depends on which implementation of IndexedCollection is in use and the types of indexes added to it.\n  \n---\n\n## Deduplicating Results ##\n\nIt is possible that a query would result in the same object being returned more than once.\n\nFor example if an object matches several attribute values specified in an `or`-type query, then the object will be returned multiple times, one time for each attribute matched. Intersections (`and`-type queries) and negations (`not`-type queries) do not produce duplicates.\n\nBy default, CQEngine does _not_ perform de-duplication of results; however it can be _instructed_ to do so, using various strategies such as Logical Elimination and Materialize. Read more: [DeduplicationStrategies](documentation/DeduplicationStrategies.md)\n\n---\n\n\n## Ordering Results ##\n\nBy default, CQEngine does not order results; it simply returns objects in the order it finds them in the collection or in indexes.\n\nCQEngine can be instructed to order results via query options as follows.\n\n**Order by price descending**\n```java\nResultSet\u003cCar\u003e results = cars.retrieve(query, queryOptions(orderBy(descending(Car.PRICE))));\n```\n\n**Order by price descending, then number of doors ascending**\n```java\nResultSet\u003cCar\u003e results = cars.retrieve(query, queryOptions(orderBy(descending(Car.PRICE), ascending(Car.DOORS))));\n```\n\nNote that ordering results as above uses the default _materialize_ ordering strategy. This is relatively expensive, dependent on the number of objects matching the query, and can cause latency in accessing the first object. It requires all results to be materialized into a sorted set up-front _before iteration can begin_.\n\n### Index-accelerated ordering ###\n\nCQEngine also has support to use an index to accelerate, or eliminate, the overhead of ordering results. This strategy reduces the latency to access the first object in the sorted results, at the expense of adding more total overhead if the entire ResultSet was iterated. Read more: [OrderingStrategies](documentation/OrderingStrategies.md)\n\n---\n## Merge Strategies ##\n\nMerge strategies are the algorithms CQEngine uses to evaluate queries which have multiple branches.\n\nBy default CQEngine will use strategies which should suit most applications, however these strategies can be overridden to tune performance. Read more: [MergeStrategies](documentation/MergeStrategies.md)\n\n---\n\n## Index Quantization, Granularity, and tuning index size ##\n\n[Quantization](http://en.wikipedia.org/wiki/Quantization_(signal_processing)) involves converting fine-grained or continuous values, to discrete or coarse-grained values. A Quantizer is a _function_ which takes fine-grained values as input, and maps those values to coarse-grained counterparts as its output, by discarding some precision.\n\nQuantization can be a useful tool to tune the size of indexes, trading a reduction in index size, for increases in CPU overhead and vice-versa. Read more: [Quantization and included Quantizers](documentation/IndexQuantization.md)\n\n\n---\n\n\n## Grouping and Aggregation (GROUP BY, SUM...) ##\n\nCQEngine has been designed with support for grouping and aggregation in mind, but note that this is not built into the CQEngine library itself, because CQEngine is designed to integrate with Java 8+ `Stream`s. This allows CQEngine results to be grouped, aggregated, and transformed in flexible ways using lambda expressions.\n\nCQEngine `ResultSet` can be converted into a Java 8 `Stream` by calling `ResultSet.stream()`.\n\nNote that Streams are **evaluated via filtering** and they do not avail of CQEngine indexes. So **for best performance, as much of the overall query as possible should be encapsulated in the CQEngine query**, as opposed to in lambda expressions in the stream. This combination would dramatically outperform a stream and lambda expression alone, which simply filtered the collection.\n\nHere's how to transform a `ResultSet` into a `Stream`, to compute the distinct set of Colors of cars which match a CQEngine query.\n```java\npublic static void main(String[] args) {\n    IndexedCollection\u003cCar\u003e cars = new ConcurrentIndexedCollection\u003c\u003e();\n    cars.addAll(CarFactory.createCollectionOfCars(10));\n    cars.addIndex(NavigableIndex.onAttribute(Car.MANUFACTURER));\n\n    Set\u003cCar.Color\u003e distinctColorsOfFordCars = cars.retrieve(equal(Car.MANUFACTURER, \"Ford\"))\n            .stream()\n            .map(Car::getColor)\n            .collect(Collectors.toSet());\n\n    System.out.println(distinctColorsOfFordCars); // prints: [GREEN, RED]\n}\n```\n\n---\n\n## Accessing Index Metadata and Statistics from MetadataEngine ##\n\nThe [MetadataEngine](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/metadata/MetadataEngine.html), is a high-level API which can retrieve metatadata and statistics from indexes which have been added to the collection.\n\nIt provides access to the following:\n  * Frequency distributions (the counts of each attribute value stored in an index)\n  * Distinct keys (the distinct attribute values in an index, optionally within a range between x and y)\n  * Streams of attribute values and associated objects stored in an index (ascending/descending order, optionally within a range between x and y)\n  * Count of distinct keys (how many distinct attribute values are in an index)\n  * Count for a specific key (how many objects match a specific attribute value)\n\nFor more information, see JavaDocs for: [MetadataEngine](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/metadata/MetadataEngine.html), [AttributeMetadata](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/metadata/AttributeMetadata.html), [SortedAttributeMetadata](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/com/googlecode/cqengine/metadata/SortedAttributeMetadata.html)\n\n---\n\n\n## Using CQEngine with Hibernate / JPA / ORM Frameworks ##\n\nCQEngine has seamless integration with JPA/ORM frameworks such as Hibernate or EclipseLink.\n\nSimply put, CQEngine can build indexes on, and query, any type of Java collection or arbitrary data source. ORM frameworks return entity objects loaded from database tables in Java collections, therefore CQEngine can act as a very fast in-memory query engine on top of such data.\n\n\n---\n\n\n## Usage in Maven and Non-Maven Projects ##\n\nCQEngine is in Maven Central, and can be added to a Maven project as follows:\n```\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.googlecode.cqengine\u003c/groupId\u003e\n    \u003cartifactId\u003ecqengine\u003c/artifactId\u003e\n    \u003cversion\u003ex.x.x\u003c/version\u003e\n\u003c/dependency\u003e\n```\nSee [ReleaseNotes](documentation/ReleaseNotes.md) for the latest version number.\n\nFor non-Maven projects, a version built with [maven-shade-plugin](http://maven.apache.org/plugins/maven-shade-plugin/) is also provided, which contains CQEngine and all of its own dependencies packaged in a single jar file (ending \"-all\"). It can be downloaded from Maven central as \"-all.jar\" [here](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.googlecode.cqengine%22%20AND%20a%3A%22cqengine%22).\n\n---\n\n## Using CQEngine in Scala, Kotlin, or other JVM languages ##\n\nCQEngine should generally be compatible with other JVM languages besides Java too, however it can be necessary to apply a few tricks to make it work. See [OtherJVMLanguages.md](documentation/OtherJVMLanguages.md) for some tips.\n\n---\n\n\n## Related Projects ##\n\n  * CQEngine is somewhat similar to [Microsoft LINQ](http://en.wikipedia.org/wiki/Language_Integrated_Query), but a difference is LINQ queries on collections are evaluated via iteration/filtering whereas CQEngine uses set theory, thus CQEngine would outperform LINQ\n\n  * [Concurrent Trees](http://github.com/npgall/concurrent-trees/) provides Concurrent Radix Trees and Concurrent Suffix Trees, used by some indexes in CQEngine\n\n\n---\n\n\n## Project Status ##\n\n  * CQEngine 3.6.0 is the current release as of writing (January 2021), and is in Maven central\n  * A [ReleaseNotes](documentation/ReleaseNotes.md) page has been added to document changes between releases\n  * API / JavaDocs are available [here](http://htmlpreview.github.io/?http://raw.githubusercontent.com/npgall/cqengine/master/documentation/javadoc/apidocs/index.html)\n\nReport any bugs/feature requests in the [Issues](http://github.com/npgall/cqengine/issues) tab.\nFor support please use the [Discussion Forum](http://groups.google.com/forum/#!forum/cqengine-discuss), not direct email to the developers.\n\n\nMany thanks to JetBrains for supporting CQEngine with free IntelliJ licenses!\n\n[![](documentation/images/logo_jetbrains.png)](http://www.jetbrains.com)[![](documentation/images/logo_intellij_idea.png)](http://www.jetbrains.com/idea/)\n","funding_links":[],"categories":["Projects","Java","项目","\u003ca name=\"Java\"\u003e\u003c/a\u003eJava"],"sub_categories":["Miscellaneous","杂项"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnpgall%2Fcqengine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnpgall%2Fcqengine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnpgall%2Fcqengine/lists"}