The LinkAhead Query Language#
Users and client applications send search queries to the LinkAhead server as plain text that follows the LinkAhead Query Language (LQL) syntax. This page gives a high to medium level overview over the syntax.
For more information, see
the web interface tutorials Finding data and Get tabular data.
Query structure#
LQL queries are structured in the following way:
Formal grammar specification#
The exact definition of these parts is best reflected by these ANTLR grammars, both from the server source code:
CQLLexer.g4for the tokenization of the input. (file on gitlab.com)CQLParser.g4for the construction of the syntax elements (file on gitlab.com)
Conditions / Filters#
Property filters#
The result set can be restricted using Property-Operator-Value (POV) filters, which filter results
based on the value of a property. The following queries are equivalent, and return records which
have a property named property1 with the value value1.
FIND entityname.property1=value1
FIND entityname WITH property1=value1
FIND entityname WHICH HAS A property1=value1
FIND entityname WHICH HAS A PROPERTY property1=value1
As described in the LQL Explanation, the result set can be restricted to any other entity role by modifying the entity expression:
FIND RECORDTYPE entityname WHICH HAS A property1=value1
Available basic operators: =, !=, <=, <, >=, > (see the following sections for LIKE,
date‑time operators, etc.).
Special operator: LIKE#
The operator LIKE can be used with wildcards. The asterisk * is a wildcard for any (possibly
empty) sequence of characters.
Examples:
FIND entityname WHICH HAS A property1 LIKE va*
FIND entityname WHICH HAS A property1 LIKE va*1
FIND entityname WHICH HAS A property1 LIKE *al1
Note
The LIKE operator is only well-defined for text properties.
Special case: references#
Reference properties can also be used for POV filters, like atomic properties. This means
FIND entityname1 WITH property1=entityname2
will return any record named entityname1 which fulfills one of these conditions:
It has a reference property named
property1which references an entity with name or identityname2.It has a text property
property1with the string valueentityname2.
To restrict the result set to reference properties, reference operators can be used:
Reference operators: ->, REFERENCES, REFERENCE TO
The query looks like this:
FIND entityname1 WHICH HAS A property1 REFERENCE TO entityname2
FIND entityname1 WHICH HAS A property1->entityname2
Special case: DateTime#
DateTime operators: =, !=, <, >, IN, NOT IN
= Equivalence relation#
datetime_1 = datetime_2
Trueifdatetime_1anddatetime_2are equal in every respect, which means they use the same DateTime type, the same fields are defined/undefined, and all defined fields are equal respectively.Falseif they have the same DateTime flavor, but have diferent fields defined, or corresponding defined fields have difering values.Undefinedotherwise.
Examples:
2015-04-03=2015-04-03T00:00:00is undefined.2015-04-03T00:00:00=2015-04-03T00:00:00.0is undefined (second precision vs. nanosecond precision).2015-04-03T00:00:00.0=2015-04-03T00:00:00.0is true.2015-04-03T00:00:00=2015-04-03T00:00:00is true.2015-04=2015-05is false.2015-04=2015-04is true.
!= Intransitive, symmetric relation#
Inverse of equivalence: datetime_1 != datetime_2
Trueifdatetime_1=datetime_2is false.Falseifdatetime_1=datetime_2is true.Undefinedotherwise.
Examples:
2015-04-03!=2015-04-03T00:00:00is undefined.2015-04-03T00:00:00!=2015-04-03T00:00:00.0is undefined.2015-04-03T00:00:00.0!=2015-04-03T00:00:00.0is false.2015-04-03T00:00:00!=2015-04-03T00:00:00is false.2015-04!=2015-05is true.2015-04!=2015-04is false.
> Transitive, non-symmetric relation#
datetime_1 > datetime_2
Semantics depend on the flavors of datetime_1 and datetime_2. If both operands are instances of:
UTCDateTime#
Trueif the time ofdatetime_1is after the time ofdatetime_2according to UTC.Falseotherwise.
SemiCompleteDateTime#
Datetime data of flavor SemiCompleteDateTime has inclusive lower bounds (ILB) and exclusive
upper bounds (EUB) defined.
Trueifdatetime_1.ILB > datetime_2.EUBordatetime_1.ILB = datetime_2.EUB.Falseifdatetime_1.EUB < datetime_2.ILBordatetime_1.EUB = datetime_2.ILB.Undefinedotherwise.
Examples:
2015>2014is true.2015-04>2014is true.2015-01-01T20:15.00>2015-01-01T20:14is true.2015-04>2015is undefined.2015>2015-04is undefined.2015-01-01T20:15>2015-01-01T20:15:15is undefined.2014>2015is false.2014-04>2015is false.2014-01-01>2015-01-01T20:15:30is false.
< Transitive, non-symmetric relation#
datetime_1 < datetime_2
Semantics depend on the flavors of datetime_1 and datetime_2 and are similar to those of >. If
both operands are instances of:
UTCDateTime#
Trueif the time ofdatetime_1is before the time ofdatetime_2according to UTCFalseotherwise.
SemiCompleteDateTime#
Datetime data of flavor SemiCompleteDateTime has inclusive lower bounds (ILB) and exclusive
upper bounds (EUB) defined.
Trueifdatetime_1.EUB < datetime_2.ILBordatetime_1.EUB = datetime_2.ILB.Falseifdatetime_1.ILB > datetime_2.EUBordatetime_1.ILB = datetime_2.EUB.Undefinedotherwise.
Examples:
2014<2015is true.2014-04<2015is true.2014-01-01<2015-01-01T20:15:30is true.2015-04<2015is undefined.2015<2015-04is undefined.2015-01-01T20:15<2015-01-01T20:15:15is undefined.2015<2014is false.2015-04<2014is false.2015-01-01T20:15.00<2015-01-01T20:14is false.
IN Transitive, non-symmetric relation.#
datetime_1 IN datetime_2
Semantics depend on the flavors of datetime_1 and datetime_2. If both are instances of:
SemiCompleteDateTime#
Trueif (datetime_1.ILB > datetime_2.ILBordatetime_1.ILB = datetime_2.ILB) and (datetime_1.EUB < datetime_2.EUBordatetime_1.EUB = datetime_2.EUB).Falseotherwise.
Examples:
2015-01-01 IN 2015is true.2015-01-01T20:15:30 IN 2015-01-01is true.2015-01-01T20:15:30 IN 2015-01-01T20:15:30is true.2015 IN 2015-01-01is false.2015-01-01 IN 2015-01-01T20:15:30is false.
NOT IN Transitive, non-symmetric relation.#
datetime_1 NOT IN datetime_2
Semantics depend on the flavors of datetime_1 and datetime_2. If both are instances of:
SemiCompleteDateTime#
Trueifdatetime_1.ILB IN datetime_2.ILBis false.Falseotherwise.
Examples:
2015 NOT IN 2015-01-01is true.2015-01-01 NOT IN 2015-01-01T20:15:30is true.2015-01-01 NOT IN 2015is false.2015-01-01T20:15:30 NOT IN 2015-01-01is false.2015-01-01T20:15:30 NOT IN 2015-01-01T20:15:30is false.
Note on three-valued logic (undefined truth values)#
LQL uses three‑valued logic (true, false, undefined) for these DateTime operators. Only
true is truth preserving, this means that only expressions which evaluate to true pass the
POV filter.
Example: FIND ... WHICH HAS A somedate=2015-01 only returns entities for which
somedate=2015-01 is true. On the other hand, FIND ... WHICH DOESN'T HAVE A somedate=2015-01
returns entities for which somedate=2015-01 is false or undefined.
In summary, NOT datetime_1 = datetime_2 is not equivalent to datetime_1 != datetime_2. The
latter assertion is stronger.
Omitting the Property or the Value#
The property or the value may also be omitted. The following equivalent queries filter the result
set for records which have any property with a value greater than value1.
FIND entityname WHICH HAS A PROPERTY > value1
FIND entityname . > value1
FIND entityname.>value1
And for references…
FIND entityname1 WHICH HAS A REFERENCE TO entityname2
FIND entityname1 WHICH REFERENCES entityname2
FIND entityname1 . -> entityname2
FIND entityname1.->entityname2
The following query returns records which have a property1 property with any value.
FIND entityname WHICH HAS A PROPERTY property1
FIND entityname WHICH HAS A property1
FIND entityname WITH property1
FIND entityname WITH A property1
FIND entityname WITH A PROPERTY property1
FIND entityname WITH PROPERTY property1
FIND entityname . property1
FIND entityname.property1
Transaction filters#
Transaction filters specify metadata concerning the creation or modification of entities.
Query structure#
FIND entityname WHICH (sugar|negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED) (by_clause time_clause?| time_clause by_clause?)
Definition#
sugarHAS BEEN|HAVE BEEN|HAD BEEN|WAS|ISnegated_sugarHAS NOT BEEN|HASN'T BEEN|WAS NOT|WASN'T|IS NOT|ISN'T|HAVN'T BEEN|HAVE NOT BEEN|HADN'T BEEN|HAD NOT BEENby_clauseBY (ME | username | SOMEONE ELSE (BUT ME)? | SOMEONE ELSE BUT username)datetimeA datetime string of the form
YYYY[-MM[-DD(T| )[hh[:mm[:ss[.nnn][(+|-)zzzz]]]]]]orTODAY.time_clause[AT|ON|IN|BEFORE|AFTER|UNTIL|SINCE] (datetime)
Examples#
FIND entityname WHICH HAS BEEN CREATED BY ME ON 2014-12-24
FIND entityname WHICH HAS BEEN CREATED BY SOMEONE ELSE ON 2014-12-24
FIND entityname WHICH HAS BEEN CREATED BY erwin ON 2014-12-24
FIND entityname WHICH HAS BEEN UPDATED BY SOMEONE ELSE BUT erwin ON 2014-12-24
FIND entityname WHICH HAS BEEN INSERTED BY erwin
FIND entityname WHICH HAS BEEN INSERTED SINCE 2021-04
Note that ON, SINCE, UNTIL are inclusive, while AT, BEFORE and AFTER are not.
File Location#
Search for file objects by their location:
FIND FILE WHICH IS STORED AT a/certain/path/
Wildcards#
STORED AT accepts Unix-style wildcards.
*matches any characters or none at all, except the directory separator/**matches any characters or none at all, including/.A leading
*is a shortcut for/**Asterisks directly between two other asterisks are ignored:
***is the same as**.Escape character:
\(E.g.\\is a literal backslash.\*is a literal star. But\\*is a literal backslash followed by a wildcard.)
Examples#
Find any files ending with .acq:
FIND FILE WHICH IS STORED AT *.acq
FIND FILE WHICH IS STORED AT **.acq
FIND FILE WHICH IS STORED AT /**.acq
Find files stored one directory below /data/, ending with .acq:
FIND FILE WHICH IS STORED AT /data/*/*.acq
Find files stored in /data/, ending with .acq:
FIND FILE WHICH IS STORED AT /data/*.acq
Find files stored in a directory at any depth in the tree below /data/, ending with .acq:
FIND FILE WHICH IS STORED AT /data/**.acq
Find any file in a directory which begins with 2016-02:
FIND FILE WHICH IS STORED AT */2016-02*/*
Backwards references#
The backwards reference filters for entities that are referenced by another entity. The following
query returns records of the type entityname1 which are referenced by entityname2 entities via
the reference property property1.
FIND entityname1 WHICH IS REFERENCED BY entityname2 AS A property1
FIND entityname1 WITH @ entityname2 / property1
FIND entityname1 . @ entityname2 / property1
The property specification may be omitted:
FIND entityname1 WHICH IS REFERENCED BY entityname2
FIND entityname1 WHICH HAS A PROPERTY @ entityname2
FIND entityname1 WITH @ entityname2
FIND entityname1 . @ entityname2
Nested queries, or filtering by sub-properties#
Nested queries can be searched by concatenating WHICH or WITH expressions:
FIND entityname WHICH HAS A property WHICH HAS A subproperty=val
For example:
FIND AN experiment WHICH HAS A camera WHICH HAS A 'serial number'= 1234567890
Combining filters with propositional logic#
Any result set can be filtered by combining POV filters or back reference filters using logical operators:
Conjunction (AND)#
As mentioned above, users can combine conditions:
FIND MusicalAnalysis WHICH HAS quality_factor>0.5 AND date IN 2019
Notes:
This operator can be used as
FIND <entity_expr> WHICH <filter1> AND <filter2>.The character
&has the same meaning and can be used instead ofAND.Several conditions can be chained together.
Mixed conjunctions and disjunctions require parentheses to define the priority. For example:
FIND <entity_expr> WHICH (<filter1> AND <filter2>) OR <filter3>.
FIND Guitar WHICH REFERENCES Manufacturer AND price is a combination of a reference filter and a
POV filter. For readability, this could also be written as
FIND Guitar WHICH REFERENCES Manufacturer AND WHICH HAS A price.
However, the additional WHICH HAS A
is syntactic sugar, which means it is purely cosmetic and does not change the meaning of the
query.
Disjunction (OR)#
The rules for disjunctions (OR or |) are the same as for conjunctions, see above.
FIND entityname1 WHICH HAS A PROPERTY property1=value1 OR A PROPERTY property2=val2 Or A PROPERTY...
FIND entityname1 WHICH HAS A PROPERTY property1=value1 OR A property2=val2 OR ...
FIND entityname1 . property1=value1 | property2=val2 | ...
Negation (NOT)#
Users can negate any filter by prefixing the filter with NOT or !:
FIND <entity_expr> WHICH NOT <filter1>
There are many syntactic sugar alternatives which are treated the same as NOT:
DOES NOT HAVEISN'Tand many more
FIND entityname1 WHICH DOES NOT HAVE A PROPERTY property1=value1
FIND entityname1 WHICH DOESN'T HAVE A property1=value1
FIND entityname1 . NOT property2=val2
FIND entityname1 . !property2=val2
Parentheses#
Users can add parentheses around filter expressions, conjunctions and disjunctions.
FIND Guitar WHICH (REFERENCES Manufacturer AND WHICH HAS A price)
FIND Guitar WHICH (REFERENCES Manufacturer) AND (WHICH HAS A price)
For better readability, the above query can be written as:
FIND Guitar WHICH (REFERENCES Manufacturer AND HAS A price).
FIND entityname1
WHICH HAS A property1=value1 AND DOESN'T HAVE A property2<val2
AND (
(WHICH HAS A property3=val3 AND A property4=val4)
OR DOES NOT HAVE A
(property5=val5 AND property6=val6)
)
FIND entityname1 . property1=value1 & !property2<val2
& (
(property3=val3 & property4=val4)
| !
(property5=val5 & property6=val6)
)
FIND entityname1.property1=value1&!property2<val2&((property3=val3&property4=val4)|!(property5=val5&property6=val6))
Whitespace was added for readability, but is not necessary.
A few important keywords#
AThe indistinct article. This is only syntactic sugar. Equivalent expressions:
A,ANANDThe logical and. Equivalent expressions:
AND,&FINDThe beginning of the query.
NOTThe logical negation. Equivalent expressions:
NOT,DOESN'T HAVE A PROPERTY,DOES NOT HAVE A PROPERTY,DOESN'T HAVE A,DOES NOT HAVE A,DOES NOT,DOESN'T,IS NOT,ISN'T,!ORThe logical or. Equivalent expressions:
OR,|RECORD,RECORDTYPE,FILE,PROPERTYRole expression for restricting the result set to a specific role.
WHICHStarts the filters section. Equivalent expressions:
WHICH,WHICH HAS A,WHICH HAS A PROPERTY,WHERE,WITH (A),.REFERENCEThis one is tricky:
REFERENCE TOexpresses a the state of having a reference property.REFERENCED BYexpresses the state of being referenced by another entity.COUNTCOUNTworks likeFINDbut doesn’t return the entities.
Select queries#
In contrast to FIND queries, which always return the complete entity, SELECT queries return the
entity with only those properties which are specified in the query.
The SELECT syntax is very similar to FIND queries - replace the FIND by
SELECT <comma separated list of selectors> FROM:
SELECT prop1, prop2, prop3 FROM entityname
However, the SELECT query can also return properties of referenced entities and thereby may join
entities together and return a custom view or projection:
SELECT Responsible.Last Name FROM Experiment
This returns the responsible person’s last name, if Responsible is a reference property of
Experiment and Last Name is a property of the Responsible records.
Selectors#
Selectors are dot‑separated entity name identifiers: Responsible.Address, Responsible.Last Name
or Experiment.Responsible.Last name.
Multiple selectors comma-separated: Responsible.First Name, Responsible.Last Name.
Evaluation of selectors#
The query will return all those properties which have the same name as specified by the selectors
(case-insensitive). However, SELECT queries are also capable of sub-typing in the selectors:
SELECT Person FROM Experiment returns all Person properties, and also all Responsible
properties, if Responsible is a child RecordType of Person.
Note
If Experiment instead has a property responsible_person with data type Person, the above
SELECT statement does not include Persons from this property, as helper is not a child of
Person.