The CCL grammar has been expanded to support parsing Concourse commands in addition to conditions, orders, and pages. A command string (e.g., select name from 1 where age > 30) is parsed into a CommandTree containing a CommandSymbol that represents the operation, along with optional ConditionTree, OrderTree, and PageTree children.
The following Concourse operations can now be expressed and parsed in CCL:
Data Modification
add- Add a value to a key in a record (e.g.,add name as jeff in 1)set- Set a value for a key in a record or records (e.g.,set name as jeff in [1, 2, 3])remove- Remove a value from a key in a record (e.g.,remove name as jeff from 1)clear- Clear all values for a key in a record (e.g.,clear name from 1)insert- Insert a JSON document (e.g.,insert {"name": "jeff"})reconcile- Reconcile values for a key in a record (e.g.,reconcile name in 1 with [jeff, bob])verify_and_swap- Atomically verify and swap a value (e.g.,verify_and_swap name as jeff to bob in 1)verify_or_set- Verify or set a value (e.g.,verify_or_set name as jeff in 1)
Data Reading
select- Select keys from records with optional conditions, ordering, and paginationget- Get key values from records at an optional timestampfind- Find records matching a condition with optional ordering and paginationfind_or_add/find_or_insert- Find records matching a condition or add/insert if none foundbrowse- Browse values for keys (e.g.,browse nameorbrowse [name, age])navigate- Navigate linked data (e.g.,navigate friends.name from 1 where age > 21)describe- Describe a record's keys at an optional timestamp (e.g.,describe 1 at "yesterday")search- Full-text search (e.g.,search name for "jeff")verify- Verify a value exists (e.g.,verify name as jeff in 1)jsonify- Export records as JSON with optional identifier inclusion
History and Auditing
audit- View the audit log for a record or key (e.g.,audit name in 1 from "2024-01-01" to "2024-02-01")chronicle- View the change history for a key in a record (e.g.,chronicle name in 1)diff- Compare data between timestamps (e.g.,diff 1 from "yesterday" to "today")revert- Revert keys in records to a prior state (e.g.,revert name in 1 at "last week")trace- Trace incoming references to a record (e.g.,trace 1)
Calculations
calculate- Perform aggregate calculations (sum,average,count,min,max) on key values with optional record filtering and conditions (e.g.,calculate sum age where department = engineering)
Links
link/unlink- Create or remove links between records (e.g.,link friends from 1 to 2)
Transactions
stage/commit/abort- Transaction control
Utility
ping- Server health checkholds- Check if records contain data (e.g.,holds 1orholds [1, 2, 3])consolidate- Merge records (e.g.,consolidate 1 2 3)inventory- List all records
Each command is represented by a dedicated CommandSymbol implementation: AddSymbol, SetSymbol, RemoveSymbol, ClearSymbol, InsertSymbol, ReconcileSymbol, VerifyAndSwapSymbol, VerifyOrSetSymbol, SelectSymbol, GetSymbol, FindSymbol, FindOrAddSymbol, FindOrInsertSymbol, BrowseSymbol, NavigateSymbol, DescribeSymbol, SearchSymbol, VerifySymbol, JsonifySymbol, AuditSymbol, ChronicleSymbol, DiffSymbol, RevertSymbol, TraceSymbol, CalculateSymbol, LinkSymbol, UnlinkSymbol, StageSymbol, CommitSymbol, AbortSymbol, PingSymbol, HoldsSymbol, ConsolidateSymbol, InventorySymbol.
Added support for parsing multiple semicolon-delimited CCL statements in a single call, similar to SQL. The new Compiler#compile(String) and Compiler#compile(String, Multimap) methods accept a CCL string containing one or more statements separated by ; and return a List<AbstractSyntaxTree> with one tree per statement.
- Semicolons (
;) are now a reserved token in the grammar. Unquoted semicolons in values are no longer permitted (use quoted strings instead).
- GH-51: Allow an optional
*suffix on navigation key segments to mark them as transitive (e.g.,children*.name,a.b*.c.d*.e,children*). Transitive segments instruct Concourse to follow links recursively until exhaustion, supporting the Transitive Navigation feature in cinchapi/concourse#632. A standalone transitive stop (e.g.,children*) is also accepted as a navigation key and is equivalent to a single-stop path whose terminal stop is transitive. AddedNavigationKeyStopto model each segment as a structured(name, transitive)pair, andNavigationKeySymbol#stops()to expose them; the existingcomponents()method is unchanged.
- Changed pagination to use canonical offset-based forms only:
limit n,skip n,offset n,skip n limit m,offset n limit m,limit m skip n, andlimit m offset n. - Updated
PageSymbolto model pagination as a required skip and an optional limit, with factories forfromSkip(int),fromLimit(int), andfromSkipLimit(int, Integer). - Removed page-number pagination syntax such as
page n,size n, andpage n size m.
- Fixed a bug that caused the
Compiler's local evaluation to fail when the condition contained navigation keys (e.g.,friend.name = jeff). Theevaluatemethod now accepts anAssociationwhosefetchmethod natively resolves dot-separated key paths by traversing nested data structures. The existingMultimap-basedevaluatemethod is still supported and automatically converts to anAssociationfor interoperability, but callers are encouraged to use theAssociationoverload directly for better performance.
- Fixed a bug that caused the
CONTAINSandNOT_CONTAINSsearch operators to fail when used with navigation keys (e.g.,mother.children contains 'foo').
- Added support for the new
CONTAINSandNOT_CONTAINSsearch operators introduced in Concourse version 0.12.0. These operators can be used directly in CCL statements with the following keywords:- CONTAINS:
contains,search,search_match,~ - NOT_CONTAINS:
not_contains,search_exclude,ncontains,~!
- CONTAINS:
- GH-47: Fixed a bug that caused navigation or function keys to not be included in a Compiler's analysis of a ConditionTree.
- Fixed a regression that caused parenthetical expressions within a
ConditioncontainingLIKEREGEX,NOT_LIKEandNOT_REGEXoperators (e.g.,a = b and (email regex email.com)) to mistakenly throw aSyntaxExceptionwhen being parsed by aCompiler.
- Fixed a regression that caused Conditions with
LIKEREGEX,NOT_LIKEandNOT_REGEXoperators followed by a whitespace containing value to be incorrectly parsed.
- Added support for parsing standalone
function statementsin a compiler. Now, the following forms will parse into aFunctionTreethat contains a symbolic representation of the function expressed in the CCL statement: function(key)produces a FunctionTree whose root node contains anIndexFunctionfunction(key, record)produces a FunctionTree whose root node contains aKeyRecordsFunctionfunction(key, record1,record2,...,recordN)produces a FunctionTree whose root node contains aKeyRecordsFunctionfunction(key, condition)produces a FunctionTree whose root node contains aKeyConditionFunctionkey \| functionproduces a FunctionTree whose root node contains anImplicitKeyRecordFunction- Fixed a bug where the paramaterized type of
KeyRecordsFunctionwas aList<String>instead of aList<Long>. - Added support for including an optional timestamp within a function value statement.
In version 3.0.0 we added support for function statements.
In Concourse, a function is an operation that is applied to a collection of values; values that can be retrived from a key stored in one or more records. Concourse functions be expressed in the following ways:
function(key)- applied to every value stored for key in every recordfunction(key, records)- applied to every value stored for key in each of therecordsfunction(key, ccl/criteria)- applied to every value stored for key in each of the records that match thecclorcriteria.function(key, record)- applied to every value stored for key in therecord
In CCL, the notion of an expression is core. A Condition is really just one or more expressions that are logically joined together in a manner that expresses clear evaluation precedence.
An expression, generally takes the form:
<key> <operator> <values>
So, in the context of a database query like select(<key1>, "<key2> <operator> <value>"), key2 and key1 are both keys, but they have different roles in the operation. In this scenario, key2 isn't returned to the caller, but is part of the evaluation expression. So, we call key2 an evaluation key. On the other hand, key1 doesn't play a role in evaluation, but is an artifact of the operation. So, we call this a operation key. As you can imagine, in more complex examples, a key can play both roles.
Similar to an evaluation key, a value that is part of an expression plays the role of evaluation value.
The roles evaluation key and evaluation value are important for understanding how functions work in CCL. Conceptually the value(s) of an expression's evaluation key are retrieved and considered in relation to the expression's operator and evaluation values to determine if the record satisfies the expression. And since functions return a value, you can imagine using a function statement as either am evaluation key or an evaluation value.
In a programming language this would be easy, but in CCL it is possible with caveats due to language ambiguity. To understand these challenges, consider the question: who's average score is greater than the average of all scores?.
This question could be answered by issuing a database query of the form find("{evaluation key} > {evaluation value}"). In this case, we know that the evaluation value should be average(score) since we want to compare against the average of all scores. Now, our query looks like find("{evaluation key} > average(score)").
But confusion abounds when we consider how to express selecting the average score of each record that is being evaluated. Concourse functions support providing an explicit record or records, but, in CCL, we don't know which records are being evaluated. To get around this, we created implicit function syntax that uses the pipe character (e.g. |) to indicate that an operation should only be applied to the key in the record that is currently being evaluated. So, our complete query would look like find("score | average > average(score)").
In an effort to avoid any ambiguity, we've adopted the following conventions:
- An implicit function statement can only be used as an evaluation key and never an evaluation value
- All other function statements can be used as an evaluation value but never as an evlauation key.
| Operation Key | Evaluation Key | Evaluation Value | |
|---|---|---|---|
function(key) |
NO | NO | YES |
function(key, records) |
NO | NO | YES |
function(key, record) |
NO | NO | YES |
function(key, ccl) |
NO | NO | YES |
key | function |
YES | YES | NO |
- This grammar has been expanded and renamed from Concourse Criteria Language to Concourse Command Language. In addition to supporting the parsing of
Conditionstatements, this grammar now supports parsing the following additional statements:- Page
- Order
A Page statement can be parsed from the following forms:
SIZE n= the first page withnitemsPAGE n= thenth page with the default number of itemsPAGE m SIZE n= themth page withnitems
An Order statement can be parsed from the following forms:
ORDER BY {key}= sort by a single keyORDER BY {key1}, {key2}, ... {keyn}= sort by multiple keysORDER BY {key} {direction}= sort by a single key withdirectionORDER BY {key1} {direction}, {key2}, ... {key3} {direction}= sort by multiple keys, each with an independent and optionaldirectionORDER BY {key} at {timestamp}= sort by a single key attimestampORDER BY {key1} at {timestamp}, {key2}, ... {keyn} at {timestamp}= sort by multiple keys, each with an independent and optionaltimestampORDER BY {key} {direction} at {timestamp}= sort by a single key attimestampwith `directionORDER BY {key1} {direction} at {timestamp}, {key2}, ... {keyn} {direction} at {timestamp}= sort by multiple keys, each with an independent and optionaltimestampanddirection
- The
Expressionsymbol has been deprecated and renamedExpressionSymbolfor clarity. - The deprecated
ConcourseParserhas been removed. - Renamed the
com.cinchapi.ccl.v2.generatedpackage tocom.cinchapi.ccl.generated. - The
Parserconstruct has been deprecated in favor of aCompiler. Compilers are superior to Parsers because they provide a superset of functonality and are stateless.
- Fixed a bug that caused erroneous parsing errors in a CCL statement containing the
REGEX,NREGEX,LIKE, orNOT_LIKEoperators followed by a string value with a parenthesis (e.g. a regex grouping character).
- Fixed a bug that caused non-numeric Tags to be erroneously parsed and transformed into symbols containing String values instead of Tag values
- Fixed a bug that caused Strings or String-like values that contained an
=(equals sign) or whitespace character to not be properly quoted in aValueSymbol.
- Fixed a bug that caused the
v2parser to fail when trying to parse a CCL statement that contained unquoted string values with periods.
- Fixed a bug that caused a
ValueSymbolcontaining aTimestampvalue to be written in a CCL format that could not be re-parsed by a CCL parser. This bug cased aSyntaxExceptionto thrown when attempting to tokenize a CCL statement generated by a Criteria that contained anyTimestampvalues.
- Added support for navigation keys. A navigation key is used to traverse the document graph in Concourse. It is made up of multiple keys that are joined by the
.character (i.e.friends.friends.name).
- Fixed a bug that caused the CCL parser to fail on certain Unicode quote characters.
- Added context about the CCL statement being processed to the exceptions thrown from operations in the
v2parser.
- Added the
Parser#evaluatemethod that performs local evaluation of the parsed query on an input dataset.
- Fixed a bug that caused both the
v1andv2parsers to mishandle numericStringandTagvalues. These values were treated as numbers instead of their actual type. This made it possible for queries containing those values to return inaccurate results.
- Added support for escaping special characters in value tokens.
- Added enforcement within the
v2parser that ensures that theLINKS_TOoperator is followed by a numeric token.
- Fixed a bug that caused the
v2parser to fail to parse CCL statements containing operator tokens named after their respective enums (e.g.LINKS_TOinstead oflnks2).
- Fixed a bug that caused the
v2parser to fail to parse CCL statements containing theLIKEandNOT_LIKEoperators.
- Fixed a bug that caused the
v2parser to incorrectly tokenize CCL statements that contained unquoted string values containing spaces.
- Deprecated
Parser#newParserstatic factory methods in favor of theParser#createstatic factory methods. - Deprecated the internal
ConcourseParserin favor of an implementation generated by thejavaccframework. This makes the parser more performant, stable and amenable to future enhancements.
- Added additional date time formatters that may be used to transform a string into a number of microseconds from the unix epoch in the
NaturalLanguage#parseMicrosmethod.
- Added support for traversing an
AbstractSyntaxTreeusing the visitor pattern. - Fixed bugs in the
AbstractSyntaxTreegeneration for theConcourseParser.
- Added a
Parsing#toPostfixNotationutility method that transforms a list ofSymbols into aQueueofPostfixNotationSymbols.
- Changed the
Parserinterface to maintain parsing state and expose instance methods instead of static functions. - Fixed a bug that caused the Parser's analyzer to not correctly detect the keys being evaluated by a CCL statement.