diff --git a/.gitlab/issue_templates/Default.md b/.gitlab/issue_templates/Default.md new file mode 100644 index 0000000000000000000000000000000000000000..aa1a65aca363b87aff50280e1a86824009d2098b --- /dev/null +++ b/.gitlab/issue_templates/Default.md @@ -0,0 +1,28 @@ +## Summary + +*Please give a short summary of what the issue is.* + +## Expected Behavior + +*What did you expect how the software should behave?* + +## Actual Behavior + +*What did the software actually do?* + +## Steps to Reproduce the Problem + +*Please describe, step by step, how others can reproduce the problem. Please try these steps for yourself on a clean system.* + +1. +2. +3. + +## Specifications + +- Version: *Which version of this software?* +- Platform: *Which operating system, which other relevant software versions?* + +## Possible fixes + +*Do you have ideas how the issue can be resolved?* diff --git a/.gitlab/merge_request_templates/Default.md b/.gitlab/merge_request_templates/Default.md new file mode 100644 index 0000000000000000000000000000000000000000..35c6d01c5904289b77fc7f1de9419ef91a1510e9 --- /dev/null +++ b/.gitlab/merge_request_templates/Default.md @@ -0,0 +1,54 @@ +# Summary + +*Insert a meaningful description for this merge request here: What is the new/changed behavior? +Which bug has been fixed? Are there related issues?* + + +# Focus + +*Point the reviewer to the core of the code change. Where should they start reading? What should +they focus on (e.g. security, performance, maintainability, user-friendliness, compliance with the +specs, finding more corner cases, concrete questions)?* + + +# Test Environment + +*How to set up a test environment for manual testing?* + + +# Check List for the Author + +Please, prepare your MR for a review. Be sure to write a summary and a focus and create gitlab +comments for the reviewer. They should guide the reviewer through the changes, explain your changes +and also point out open questions. For further good practices have a look at [our review +guidelines](https://gitlab.com/caosdb/caosdb/-/blob/dev/REVIEW_GUIDELINES.md) + +- [ ] All automated tests pass +- [ ] Reference related issues +- [ ] Up-to-date CHANGELOG.md (or not necessary) +- [ ] Up-to-date JSON schema (or not necessary) +- [ ] Appropriate user and developer documentation (or not necessary) + - How do I use the software? Assume "stupid" users. + - How do I develop or debug the software? Assume novice developers. +- [ ] Annotations in code (Gitlab comments) + - Intent of new code + - Problems with old code + - Why this implementation? + + +# Check List for the Reviewer + +- [ ] I understand the intent of this MR +- [ ] All automated tests pass +- [ ] Up-to-date CHANGELOG.md (or not necessary) +- [ ] Appropriate user and developer documentation (or not necessary) +- [ ] The test environment setup works and the intended behavior is reproducible in the test + environment +- [ ] In-code documentation and comments are up-to-date. +- [ ] Check: Are there specifications? Are they satisfied? + +For further good practices have a look at [our review guidelines](https://gitlab.com/caosdb/caosdb/-/blob/dev/REVIEW_GUIDELINES.md). + + +/assign me +/target_branch dev diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d1e9b9072e1dcd6c562af66837b5c65a1e410e1..3de64ed7d8a375383c231a3eef455853485e9c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `FIND <FIND_QUERY_DEFAULT_ROLE>`. * Of course, administrators can choose to retain the old behavior by setting `FIND_QUERY_DEFAULT_ROLE=ENTITY`. +* CQL now treats `WITH` and `WITH A` equivalently. This is for [#192](https://gitlab.com/caosdb/caosdb-server/-/issues/192). ### Deprecated ### @@ -32,6 +33,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Documentation ### +- Nested queries. + ## [0.9.0] - 2023-01-19 ### Added diff --git a/doc/Query.md b/doc/Query.md new file mode 100644 index 0000000000000000000000000000000000000000..1a875bf3008be0ef2b078ccae2b713d533acfaf4 --- /dev/null +++ b/doc/Query.md @@ -0,0 +1,355 @@ + +# Example queries + +## Simple FIND Query +The following query will return any entity which has the name _ename_ and all its children. +`FIND ename` + +The following queries are equivalent and will return any entity which has the name _ename_ and all its children, but only if they are genuin records. Of course, the returned set of entities (henceforth referred to as _resultset_) can also be restricted to recordtypes, properties and files. + +`FIND RECORD ename` + +`FIND RECORDS ename` + +Wildcards use `*` for any characters or none at all. Wildcards for single characters (like the '_' wildcard from mysql) are not implemented yet. + +`FIND RECORD en*` returns any entity which has a name beginning with _en_. + +Regular expressions must be surrounded by _<<_ and '>>': + +`FIND RECORD <<e[aemn]{2,5}>>` + +`FIND RECORD <<[cC]am_[0-9]*>>` + +*TODO* (Timm): +Describe escape sequences like `\\`, `\*`, `\<<` and `\>>`. + +Currently, wildcards and regular expressions are only available for the _simple-find-part_ of the query, i. e. no wildcards/regexps for filters. + +## Simple COUNT Query + +This query counts entities which have certain properties. + +`COUNT ename` +will return the number of entities which have the name _ename_ and all their children. + +The syntax of the COUNT queries is equivalent to the FIND queries in any respect (this also applies to wildcards and regular expressions) but one: The prefix is to be `COUNT` instead of `FIND`. + +Unlike the FIND queries, the COUNT queries do not return any entities. The result of the query is the number of entities which _would be_ returned if the query was a FIND query. + +## Filters + +### POV - Property-Operator-Value + +The following queries are equivalent and will restrict the result set to entities which have a property named _pname1_ that has a value _val1_. + +`FIND ename.pname1=val1` + +`FIND ename WITH pname1=val1` + +`FIND ename WHICH HAS A PROPERTY pname1=val1` + +`FIND ename WHICH HAS A pname1=val1` + +Again, the resultset can be restricted to records: + +`FIND RECORD ename WHICH HAS A pname1=val1` + +_currently known operators:_ `=, !=, <=, <, >=, >` (and cf. next paragraphes!) + +#### Special Operator: LIKE + +The _LIKE_ can be used with wildcards. The `*` is a wildcard for any (possibly empty) sequence of characters. Examples: + +`FIND RECORD ename WHICH HAS A pname1 LIKE va*` + +`FIND RECORD ename WHICH HAS A pname1 LIKE va*1` + +`FIND RECORD ename WHICH HAS A pname1 LIKE *al1` + +_Note:_ The _LIKE_ operator is will only produce expectable results with text properties. + +#### Special Case: References + +In general a reference can be addressed just like a POV filter. So + +`FIND ename1.pname1=ename2` + +will also return any entity named _ename1_ which references the entity with name or id _ename2_ via a reference property named _pname1_. However, it will also return any entity with a text property of that name with the string value _ename2_. In order to restrict the result set to reference properties one may make use of special reference operators: + +_reference operators:_ `->, REFERENCES, REFERENCE TO` + + +The query looks like this: + +`FIND ename1 WHICH HAS A pname1 REFERENCE TO ename2` + +`FIND ename1 WHICH HAS A pname1->ename2` + +#### Time Special Case: DateTime + +_DateTime operators:_ `=, !=, <, >, IN, NOT IN` + +##### `d1=d2`: Equivalence relation. +* ''True'' iff d1 and d2 are equal in every respect (same DateTime flavor, same fields are defined/undefined and all defined fields are equal respectively). +* ''False'' iff they have the same DateTime flavor but have different fields defined or fields with differing values. +* ''Undefined'' otherwise. + +Examples: +* `2015-04-03=2015-04-03T00:00:00` is undefined. +* `2015-04-03T00:00:00=2015-04-03T00:00:00.0` is undefined (second precision vs. nanosecond precision). +* `2015-04-03T00:00:00.0=2015-04-03T00:00:00.0` is true. +* `2015-04-03T00:00:00=2015-04-03T00:00:00` is true. +* `2015-04=2015-05` is false. +* `2015-04=2015-04` is true. + +##### `d1!=d2`: Intransitive, symmetric relation. +* ''True'' iff `d1=d2` is false. +* ''False'' iff `d1=d2` is true. +* ''Undefined'' otherwise. + +Examples: +* `2015-04-03!=2015-04-03T00:00:00` is undefined. +* `2015-04-03T00:00:00!=2015-04-03T00:00:00.0` is undefined. +* `2015-04-03T00:00:00.0!=2015-04-03T00:00:00.0` is false. +* `2015-04-03T00:00:00!=2015-04-03T00:00:00` is false. +* `2015-04!=2015-05` is true. +* `2015-04!=2015-04` is false. + +##### `d1>d2`: Transitive, non-symmetric relation. +Semantics depend on the flavors of d1 and d2. If both are... +###### [UTCDateTime](Datatype#datetime) +* ''True'' iff the time of d1 is after the the time of d2 according to [https://en.wikipedia.org/wiki/Coordinated_Universal_Time](UTC) +* ''False'' otherwise. + +###### [SemiCompleteDateTime](Datatype#datetime) +* ''True'' iff `d1.ILB>d2.EUB` is true or `d1.ILB=d2.EUB` is true. +* ''False'' iff `d1.EUB<d2.ILB}} is true or {{{d1.EUB=d2.ILB` is true. +* ''Undefined'' otherwise. + +Examples: +* `2015>2014` is true. +* `2015-04>2014` is true. +* `2015-01-01T20:15.00>2015-01-01T20:14` is true. +* `2015-04>2015` is undefined. +* `2015>2015-04` is undefined. +* `2015-01-01T20:15>2015-01-01T20:15:15` is undefined. +* `2014>2015` is false. +* `2014-04>2015` is false. +* `2014-01-01>2015-01-01T20:15:30` is false. + +##### `d1<d2`: Transitive, non-symmetric relation. +Semantics depend on the flavors of d1 and d2. If both are... +###### [UTCDateTime](Datatype#datetime) +* ''True'' iff the time of d1 is before the the time of d2 according to [UTC](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) +* ''False'' otherwise. + +###### [SemiCompleteDateTime](Datatype#datetime) +* ''True'' iff `d1.EUB<d2.ILB` is true or `d1.EUB=d2.ILB` is true. +* ''False'' iff `d1.ILB>d2.EUB}} is true or {{{d1.ILB=d2.EUB` is true. +* ''Undefined'' otherwise. + +Examples: +* `2014<2015` is true. +* `2014-04<2015` is true. +* `2014-01-01<2015-01-01T20:15:30` is true. +* `2015-04<2015` is undefined. +* `2015<2015-04` is undefined. +* `2015-01-01T20:15<2015-01-01T20:15:15` is undefined. +* `2015<2014` is false. +* `2015-04<2014` is false. +* `2015-01-01T20:15.00<2015-01-01T20:14` is false. + +##### `d1 IN d2`: Transitive, non-symmetric relation. +Semantics depend on the flavors of d1 and d2. If both are... +###### [SemiCompleteDateTime](Datatype#datetime) +* ''True'' iff (`d1.ILB>d2.ILB` is true or `d1.ILB=d2.ILB` is true) and (`d1.EUB<d2.EUB` is true or `d1.EUB=d2.EUB` is true). +* ''False'' otherwise. + +Examples: +* `2015-01-01 IN 2015` is true. +* `2015-01-01T20:15:30 IN 2015-01-01` is true. +* `2015-01-01T20:15:30 IN 2015-01-01T20:15:30` is true. +* `2015 IN 2015-01-01` is false. +* `2015-01-01 IN 2015-01-01T20:15:30` is false. + +##### `d1 NOT IN d2`: Transitive, non-symmetric relation. +Semantics depend on the flavors of d1 and d2. If both are... +###### [SemiCompleteDateTime](Datatype#datetime) +* ''True'' iff (`d1.ILB IN d2.ILB` is false. +* ''False'' otherwise. + +Examples: +* `2015 NOT IN 2015-01-01` is true. +* `2015-01-01 NOT IN 2015-01-01T20:15:30` is true. +* `2015-01-01 NOT IN 2015` is false. +* `2015-01-01T20:15:30 NOT IN 2015-01-01` is false. +* `2015-01-01T20:15:30 NOT IN 2015-01-01T20:15:30` is false. + +##### Note +These semantics follow a three-valued logic with ''true'', ''false'' and ''undefined'' as truth values. Only ''true'' is truth preserving. I.e. only those expressions which evaluate to ''true'' pass the POV filter. `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. Shortly put, `NOT d1=d2` is not equivalent to `d1!=d2`. The latter assertion is stronger. + +#### Omitting the Property or the Value + +One doesn't have to specify the property or the value at all. The following query filters the result set for entities which have any property with a value greater than _val1_. + +`FIND ename WHICH HAS A PROPERTY > val1` + +`FIND ename . > val1` + +`FIND ename.>val1` + + +And for references... + +`FIND ename1 WHICH HAS A REFERENCE TO ename2` + +`FIND ename1 WHICH REFERENCES ename2` + +`FIND ename1 . -> ename2` + +`FIND ename1.->ename2` + + +The following query returns entities which have a _pname1_ property with any value. + +`FIND ename WHICH HAS A PROPERTY pname1` + +`FIND ename WHICH HAS A pname1` + +`FIND ename WITH pname1` + +`FIND ename WITH A pname1` + +`FIND ename WITH A PROPERTY pname1` + +`FIND ename WITH PROPERTY pname1` + +`FIND ename . pname1` + +`FIND ename.pname1` + +### TransactionFilter + +*Definition* + sugar:: `HAS BEEN` | `HAVE BEEN` | `HAD BEEN` | `WAS` | `IS` + negated_sugar:: `HAS NOT BEEN` | `HASN'T BEEN` | `WAS NOT` | `WASN'T` | `IS NOT` | `ISN'T` | `HAVN'T BEEN` | `HAVE NOT BEEN` | `HADN'T BEEN` | `HAD NOT BEEN` + by_clause:: `BY (ME | username | SOMEONE ELSE (BUT ME)? | SOMEONE ELSE BUT username)` + datetime:: A datetime string of the form `YYYY[-MM[-DD(T| )[hh[:mm[:ss[.nnn][(+|-)zzzz]]]]]]` + time_clause:: `[AT|ON|IN|BEFORE|AFTER|UNTIL|SINCE] (datetime) ` + +`FIND ename WHICH (sugar|negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED) (by_clause time_clause?| time_clause by_clause?)` + +*Examples* + +`FIND ename WHICH HAS BEEN CREATED BY ME ON 2014-12-24` + +`FIND ename WHICH HAS BEEN CREATED BY SOMEONE ELSE ON 2014-12-24` + +`FIND ename WHICH HAS BEEN CREATED BY erwin ON 2014-12-24` + +`FIND ename WHICH HAS BEEN CREATED BY SOMEONE ELSE BUT erwin ON 2014-12-24` + +`FIND ename WHICH HAS BEEN CREATED BY erwin` + +`FIND ename WHICH HAS BEEN INSERTED SINCE 2021-04` + +Note that `SINCE` and `UNTIL` are inclusive, while `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_ can be used with wildcards similar to unix wildcards. + * `*` matches any characters or none at all, but not the directory separator `/` + * `**` matches any character or none at all. + * A leading `*` is short cut for `/**` + * A star directly between two other stars is 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` or +`FIND FILE WHICH IS STORED AT **.acq` or +`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*/*` + + +### Back References + +The back reference filters for entities that are referenced by another entity. The following query returns entities of the type _ename1_ which are referenced by _ename2_ entities via the reference property _pname1_. + +* `FIND ename1 WHICH IS REFERENCED BY ename2 AS A pname1` +* `FIND ename1 WITH @ ename2 / pname1` +* `FIND ename1 . @ ename2 / pname1` + +One may omit the property specification: + +* `FIND ename1 WHICH IS REFERENCED BY ename2` +* `FIND ename1 WHICH HAS A PROPERTY @ ename2` +* `FIND ename1 WITH @ ename2` +* `FIND ename1 . @ ename2` + +### Combining Filters with Propositional Logic + +Any result set can be filtered by logically combining POV filters or back reference filters: + +#### Conjunction (AND) + +* `FIND ename1 WHICH HAS A PROPERTY pname1=val1 AND A PROPERTY pname2=val2 AND A PROPERTY...` +* `FIND ename1 WHICH HAS A PROPERTY pname1=val1 AND A pname2=val2 AND ...` +* `FIND ename1 . pname1=val1 & pname2=val2 & ...` + +#### Disjunction (OR) + +* `FIND ename1 WHICH HAS A PROPERTY pname1=val1 OR A PROPERTY pname2=val2 Or A PROPERTY...` +* `FIND ename1 WHICH HAS A PROPERTY pname1=val1 OR A pname2=val2 OR ...` +* `FIND ename1 . pname1=val1 | pname2=val2 | ...` + +#### Negation (NOT) + +* `FIND ename1 WHICH DOES NOT HAVE A PROPERTY pname1=val1` +* `FIND ename1 WHICH DOESN'T HAVE A pname1=val1` +* `FIND ename1 . NOT pname2=val2` +* `FIND ename1 . !pname2=val2` + +#### ... and combinations with parentheses + +* `FIND ename1 WHICH HAS A pname1=val1 AND DOESN'T HAVE A pname2<val2 AND ((WHICH HAS A pname3=val3 AND A pname4=val4) OR DOES NOT HAVE A (pname5=val5 AND pname6=val6))` +* `FIND ename1 . pname1=val1 & !pname2<val2 & ((pname3=val3 & pname4=val4) | !(pname5=val5 & pname6=val6))` +* `FIND ename1.pname1=val1&!pname2<val2&((pname3=val3&pname4=val4)|!(pname5=val5&pname6=val6))` + +### A Few Important Expressions + +* A:: The indistinct article. This is only syntactic suger. Equivalent expressions: `A, AN` +* AND:: The logical _and_. Equivalent expressions: `AND, &` +* FIND:: The beginning of the query. +* NOT:: The 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, !` +* OR:: The logical _or_. Equivalent expressions: `OR, |` +* RECORD,RECORDTYPE,FILE,PROPERTY:: Role expression for restricting the result set to a specific role. +* WHICH:: The marker for the beginning of the filters. Equivalent expressions: `WHICH, WHICH HAS A, WHICH HAS A PROPERTY, WHERE, WITH (A), .` +* REFERENCE:: This one is tricky: `REFERENCE TO` expresses a the state of _having_ a reference property. `REFERENCED BY` expresses the state of _being_ referenced by another entity. +* COUNT:: `COUNT` works like `FIND` but doesn't return the entities. + +# Future + + * *Sub Queries* (or *Sub Properties*): `FIND ename WHICH HAS A pname WHICH HAS A subpname=val`. This is like: `FIND AN experiment WHICH HAS A camera WHICH HAS A 'serial number'= 1234567890` + * *More Logic*, especially `ANY`, `ALL`, `NONE`, and `SUCH THAT` key words (and equivalents) for logical quantisation: `FIND ename1 SUCH THAT ALL ename2 WHICH HAVE A REFERENCE TO ename1 HAVE A pname=val`. This is like `FIND experiment SUCH THAT ALL person WHICH ARE REFERENCED BY THIS experiment AS conductor HAVE AN 'academic title'=professor.` + diff --git a/src/doc/CaosDB-Query-Language.md b/src/doc/CaosDB-Query-Language.md index 415c555527a7b5dfcb049c507e39e8a639047550..40997cdccdb3e2c200b8112f4c8bdcfc19e10069 100644 --- a/src/doc/CaosDB-Query-Language.md +++ b/src/doc/CaosDB-Query-Language.md @@ -325,6 +325,13 @@ One may omit the property specification: * `FIND ename1 WITH @ ename2` * `FIND ename1 . @ ename2` +### Nested queries, or filtering by sub-properties ### + +Nested queries can easily be searched by simply concatenating `WHICH` or `WITH` expressions: + +* `FIND ename WHICH HAS A pname WHICH HAS A subpname=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 logically combining POV filters or back reference filters: @@ -406,12 +413,6 @@ Since Caosdb 0.2 entities are optionally version controlled. The query language * The `ANY VERSION OF` modifier currently the only expression for taking the versioning into account when using the query language. * Subproperties are not supported yet, e.g. `FIND ANY VERSION OF ENTITY WHICH IS REFERENCED BY ename WITH ...`. This applies to all cases where you specify properties of *referenced* entities or *referencing* entities. -### Future - -* Add `(LATEST|LAST|OLDEST|NEWEST|FIRST) VERSION OF` modifiers. -* Add `(ANY|LATEST|LAST|OLDEST|NEWEST|FIRST) VERSION (BEFORE|AFTER) (<timestamp>|<transaction id>|<entity@version>) OF` modifier. -* Add support for subproperties, e.g. `FIND ANY VERSION OF ENTITY WHICH IS REFERENCED BY ename WITH ...`. - ## Configuration In CaosDB Server implementations before version 0.9.0, the `FIND ename` query @@ -423,5 +424,10 @@ to `FIND RECORD ename`. This default is, however, configurable via the ## Future - * *Sub Queries* (or *Sub Properties*): `FIND ename WHICH HAS A pname WHICH HAS A subpname=val`. This is like: `FIND AN experiment WHICH HAS A camera WHICH HAS A 'serial number'= 1234567890` +* Additional versioning queries: + * Add `(LATEST|LAST|OLDEST|NEWEST|FIRST) VERSION OF` modifiers. + * Add `(ANY|LATEST|LAST|OLDEST|NEWEST|FIRST) VERSION (BEFORE|AFTER) (<timestamp>|<transaction + id>|<entity@version>) OF` modifier. + * Add support for subproperties, e.g. `FIND ANY VERSION OF ENTITY WHICH IS REFERENCED BY ename + WITH ...`. * *More Logic*, especially `ANY`, `ALL`, `NONE`, and `SUCH THAT` key words (and equivalents) for logical quantisation: `FIND ename1 SUCH THAT ALL ename2 WHICH HAVE A REFERENCE TO ename1 HAVE A pname=val`. This is like `FIND experiment SUCH THAT ALL person WHICH ARE REFERENCED BY THIS experiment AS conductor HAVE AN 'academic title'=professor.` diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4 index 6851adcf0f1b55ddcf7cfc83785ebbc819e84621..95189d538990c3821028f85de5747f699adb6826 100644 --- a/src/main/java/org/caosdb/server/query/CQLLexer.g4 +++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4 @@ -333,6 +333,11 @@ WITH: [Ww][Ii][Tt][Hh] WHITE_SPACE_f? ; +/** */ +WITH_A: + [Ww][Ii][Tt][Hh] (WHITE_SPACE_f? A)? WHITE_SPACE_f? +; + /** */ THE: [Tt][Hh][Ee] WHITE_SPACE_f? @@ -368,7 +373,6 @@ ELSE: [Ee][Ll][Ss][Ee] WHITE_SPACE_f? ; -/** */ WHERE: [Ww][Hh][Ee][Rr][Ee] WHITE_SPACE_f? ; diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4 index 1e5a732140588682abe23bc32d66db3d5104c9c8..bcb4645ad7019d61f933793989e5d0afa5521f09 100644 --- a/src/main/java/org/caosdb/server/query/CQLParser.g4 +++ b/src/main/java/org/caosdb/server/query/CQLParser.g4 @@ -153,7 +153,7 @@ entity_filter returns [EntityFilterInterface filter] which_exp: WHICH (HAS_A (PROPERTY)?)? | HAS_A (PROPERTY)? - | WITH (A (PROPERTY)?)? + | WITH_A (PROPERTY)? | WHERE | DOT WHITE_SPACE? ; @@ -620,7 +620,7 @@ number_with_unit */ unit : - (~(WHITE_SPACE | WHICH | HAS_A | WITH | WHERE | DOT | AND | OR | RPAREN )) + (~(WHITE_SPACE | WHICH | HAS_A | WITH_A | WHERE | DOT | AND | OR | RPAREN )) (~(WHITE_SPACE))* | NUM SLASH (~(WHITE_SPACE))+