Releases: typedb/typeql
1.0.1
Distribution (for Java)
<repositories>
<repository>
<id>repo.grakn.ai</id>
<url>https://repo.grakn.ai/repository/maven/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.graql</groupId>
<artifactId>lang</artifactId>
<version>1.0.1</version>
</dependency>
</dependencies>
Changelog
- We can now define type labels using UTF-8 character set (PR 59)
- Restrict the grammars for type labels and ID values (PR 60)
- Fixed a bug where adding an
Aggregate
object to aSet
causes aNullPointerException
to be thrown (PR 29, PR 30):GraqlGet.Aggregate query = Graql.match(var("x").isa("movie")).get().count("x"); Set<GraqlGet> queries = new HashSet<>(); queries.add(query); // no longer throws a NullPointerException
- Renamed the following methods in
Variable
(PR 54):asUserDefined()
toasReturnedVar()
to better reflect the functionality, which is to return the variables that have been added to theget
clause.isUserDefinedName()
toisReturned()
to better reflect the functionality, which is to check if a particular variable has been added to theget
clause.
Graql 1.0.0
Independent, more elegant and strongly-typed
After 3 years of work, Graql now stands as an independent technology and gets its own GitHub repository! Given the amount of work that has gone into Graql, and the stage where it's at right now, we're proud to call it Graql 1.0.0.
Hit that star button, everyone! https://github.com/graknlabs/graql
Distribution (for Java)
<repositories>
<repository>
<id>repo.grakn.ai</id>
<url>https://repo.grakn.ai/repository/maven/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.graql</groupId>
<artifactId>lang</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
Architecture
Graql now stands on its own, independent from Grakn. However, Grakn uses Graql to enable the querying of the database. Think of Graql to Grakn as SQL to MySQL; Graql is the language, and Grakn is the database that implements Graql. This means, when we speak of "Graql", we could be referring to 2 different things:
graql.lang
: the "graql language"; the library that a user would use to build graql "query objects".grakn.core.graql
: Grakn's "implementation" of the Graql language. This module/codebase contains the Graql algorithms and data structures (query engine, reasoner, analytics, etc.)l
Graql, the language, will now have its own product lifecycle, strictly as a language and independent from Grakn. Going forward, Graql becomes a dependency of Grakn.
Part of this architectural change also meant that we had to break the dependency from Graql language API to Grakn transaction API. You can no longer call query.execute()
; instead, you pass a Graql query to a Grakn transaction: transaction.execute(query)
. Thus, importing Graql into your (Java) codebase becomes lighter, as it no longer brings in massive transitive dependencies. Your architecture (that relies on Graql) should be simpler, lighter and more reliable.
Most importantly, we can start centralising all Graql plugins in this repository, making it easier for us to maintain them with every change in Graql. So for those of you who have built Graql plugins for various environments, we would love to invite you to contribute to the new Graql repo!
API/Language
Graql's grammar has been significantly refined in this release. The higher level structure of the grammar has remained mostly the same, but the grammar definition has been refined to be more strongly-typed, elegant and easily understood.
You can find the full grammar definition in graknlabs/[email protected]:grammar/Graql.g4, in which we will review the changes below.
First of all, Graql parser method (Graql.parse()
) expects a non-empty string, and the parse-list method (Graql.parseList()
) expects at least one query/pattern. The order in which queries and patterns are written before parsing is now maintained in memory and string output of the queries/patterns.
eof_query : query EOF ;
eof_query_list : query+ EOF ;
eof_pattern : pattern EOF ;
eof_pattern_list : pattern+ EOF ;
As you can see at the top of the grammar file, Graql queries are now clearly defined as:
query : query_define | query_undefine
| query_insert | query_delete
| query_get | query_get_aggregate
| query_get_group | query_get_group_agg
| query_compute ;
Define and Undefine Queries are defined to accept statement_type
s (statements that describe types in the schema). Statements that do not strictly describe types are no longer accepted at parse time.
query_define : DEFINE statement_type+ ;
query_undefine : UNDEFINE statement_type+ ;
Insert Queries are defined as a set of statement_instance
s (statements that describe data instances) that could follow a match clause (which accepts pattern
s). Statements that do not strictly describe instances are no longer accepted at parse time.
query_insert : MATCH pattern+ INSERT statement_instance+
| INSERT statement_instance+ ;
Delete and Get queries shares the same grammar, based on the match clause of a set of pattern
s, except that one results in concepts being deleted, the other results in concepts being retrieved.
query_delete : MATCH pattern+ DELETE variables filters ;
query_get : MATCH pattern+ GET variables filters ;
As you can see above, both Delete and Get Queries can be followed by "filters" to sort
, offset
, and limit
the query results. The new filters syntax are written after at the end of the query, and can only be optionally written once in the described order - a more strict definition of the syntax that maintains its full semantic expressivity. The variable provided in the sort
filter will also be checked to be within the scope of the query.
filters : sort? offset? limit? ;
sort : SORT VAR_ ORDER_? ';' ;
offset : OFFSET INTEGER_ ';' ;
limit : LIMIT INTEGER_ ';' ;
Group and Aggregate Queries now behave as functions applied to the results of a Get Query. group
and aggregate
are now independent from each other, and each function simply follows a Get Query with its function name and optionally followed by a variable, e.g. max $x
, or group $x; min $y;
.
query_get_aggregate : query_get function_aggregate ;
query_get_group : query_get function_group ;
query_get_group_agg : query_get function_group function_aggregate ;
With the new grammar, we are now able to filter the answer set in the Get Query, prior to applying an aggregate
or group
function. The variables provided in aggregate
/group
must be within the scope of the get
query, but given that get ...;
can take multiple variables and returns a unique set of answers, group
and aggregate
queries are now much more expressive than before. For example:
match $x isa person, has age $y, has email $z; get $x, $y; sum $y;
match $x isa person, has email $y, has email-provider $z; get $y, $z; group $z; count;
A pattern
, which is provided in a match clause could either be a conjunction, disjunction, negation, or statement describing types or instances.
patterns : pattern+ ;
pattern : pattern_statement
| pattern_conjunction
| pattern_disjunction
| pattern_negation
;
pattern_statement : statement_type
| statement_instance ;
A conjunction or disjunction pattern is strictly determined by the curly brackets ({
and }
) that wrap a set of patterns and the or
keyword operator. Any set of patterns wrapped in{
and }
are conjunctions, and any set of conjunctions separated by the or
keyword form a disjunction. Notice that a semicolon ;
terminates each pattern.
pattern_conjunction : '{' patterns '}' ';' ;
pattern_disjunction : '{' patterns '}' ( OR '{' patterns '}' )+ ';' ;
We have also introduced negation patterns using the not
keyword. Positive (non-negated) patterns are interpreted as the existence an instance of that pattern, negative (negated) patterns are interpreted as the absence of an instance of that pattern. Negation patterns in rules are allowed, as long as there is no recursion taking place within negated patterns. For more information, see: https://dev.grakn.ai/docs/pattern/negation.
pattern_negation : NOT '{' patterns '}' ';' ;
Type statements, which could be provided in a Define Query or a match clause, are now defined more strictly as type
(which could either be a label
or variable in a match clause, but only label
in a Define Query), followed by one or more type_properties
, strictly delimited by a comma (,
).
statement_type : type type_property ( ',' type_property )* ';' ;
You can find the full definition of type_property
L101:L112. The following is an example of a type statement which you will write in a schema definition:
person sub entity, has key username, has name, plays employee;
We've also introduced a variation of the sub
keyword: sub!
. sub!
allows you to query for the "direct" supertype of a given type. E.g.: If a sportscar sub car
, car sub vehicle
, then match sportscar sub! $x; get $x;
will only return car
, instead of all supertypes of sportscar
which are {car, vehicle}
.
Instance statements, which could be provided in an insert query or a match clause, are now defined more strictly as a statement that either describes a thing
(entity
, relation
, or attribute
), relation
, or attribute
. Each of the instance statements can be followed by one or more attribute
s, strictly delimited by a comma (,
).
statement_instance : statement_thing
| statement_relation
| statement_attribute
;
A statement_thing
needs to always begin with a variable (full definition: L120:L124). For example:
$x isa some-thing, has some-attribute;
A statement_relation
may or may not start with a variable, but needs to have role-players declared before the concept type and attributes (full definition: L125:L128). For example:
$x (role1: $y, role2: $z) isa some-relation;
(role1: $y, role2: $z) isa some-relatio...