diff --git a/CHANGELOG.md b/CHANGELOG.md
index b10d028facf3449cdd68908ac2079d5dbc7aa1c1..b91d1dcac52b8b1d4596570507fce17d75d77dce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,7 +11,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed ###
 
-- CQL now treats `WITH` and `WITH A` equivalently. This is for [#192](https://gitlab.com/caosdb/caosdb-server/-/issues/192).
+* The default behavior of the query FIND SomeName [...] (as well as COUNT and SELECT) is being made configurable and changes
+  * `FIND SomeName` will be interpreted as `FIND <FIND_QUERY_DEFAULT_ROLE>
+    SomeName` from now on where `FIND_QUERY_DEFAULT_ROLE` is a newly introduced
+    server property.
+  * The new `FIND_QUERY_DEFAULT_ROLE` server property defaults to `RECORD`
+    which is why the behavior of the server api has a **breaking change**.
+  * The semantics of `FIND *` are affected as well. `FIND *` is equivalent to
+    `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 ###
 
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 57744250a7ef88285b343aa5c037de9cbbf97097..46c0f83d4b7327d84c9009bdcba10b54ee0df70f 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -211,6 +211,15 @@ USER_NAME_INVALID_MESSAGE=User names must have a length from 1 to 32 characters.
 PASSWORD_VALID_REGEX=^((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\\p{Punct}]).{8,128})$
 PASSWORD_INVALID_MESSAGE=Passwords must have a length from 8 to 128 characters. THe must contain at least one [A-Z], one [a-z], one [0-9] and a special character e.g. [!§$%&/()=?.;,:#+*+~].
 
+# --------------------------------------------------
+# Query Settings
+# --------------------------------------------------
+
+# FIND blablabla is short for FIND $FIND_QUERY_DEFAULT_ROLE blablabla. Should be
+# either ENTITY or RECORD. RECORDTYPE, FILE, and PROPERTY will work as well but
+# are rather unexpected for human users.
+FIND_QUERY_DEFAULT_ROLE=RECORD
+
 # --------------------------------------------------
 # Extensions
 # --------------------------------------------------
@@ -220,4 +229,4 @@ ENTITY_VERSIONING_ENABLED=true
 
 
 # Enabling the state machine extension
-# EXT_STATE_ENTITY=ENABLE
+# EXT_STATE_ENTITY=ENABLE
\ No newline at end of file
diff --git a/src/doc/CaosDB-Query-Language.md b/src/doc/CaosDB-Query-Language.md
index fbccdc5554c64f4a51ed34e86798d169af37d601..a34191d051fec9907a6cbb47f11f97d60ac972ac 100644
--- a/src/doc/CaosDB-Query-Language.md
+++ b/src/doc/CaosDB-Query-Language.md
@@ -1,49 +1,66 @@
-# CaosDB Query Language
-**WIP This is going to be the specification. CQL tutorials are in the webui**
+# CaosDB Query Language Examples
 
-## Example queries
+See syntax specification in [CaosDB Query Language Syntax](query-syntax).
 
-### Simple FIND Query
-The following query will return any entity which has the name _ename_ and all its children.
-`FIND ename`
+## Simple FIND Query
 
-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.
+The following query will return any record which has the name _somename_ and all
+record children of any entity with that name.
 
-`FIND RECORD ename`
+`FIND somename`
 
-`FIND RECORDS ename`
+On server in the default configuration, the following queries are equivalent to this one.
 
-Wildcards use `*` for any characters or none at all. Wildcards for single characters (like the '_' wildcard from mysql) are not implemented yet.
+`FIND RECORD somename`
 
-`FIND RECORD en*` returns any entity which has a name beginning with _en_.
+`FIND RECORDS somename`
 
-Regular expressions must be surrounded by _<<_ and '>>':
+Of course, the returned set of entities (henceforth referred to as _resultset_) can also be
+restricted to RecordTypes (`FIND RECORDTYPE ...`), Properties (`FIND PROPERTY ...`) and Files (`FIND
+FILE ...`).
 
-`FIND RECORD <<e[aemn]{2,5}>>`
+You can include all entities (Records, RecordTypes, Properties, ...) into the results by using the
+`ENTITY` keyword:
 
-`FIND RECORD <<[cC]am_[0-9]*>>`
+`FIND ENTITY somename`
 
-*TODO* (Timm):
-Describe escape sequences like `\\  `, `\*`, `\<<` and `\>>`.
+Wildcards use `*` for any characters or none at all. Wildcards for single characters (like the `_` wildcard from mysql) are not implemented yet.
 
-Currently, wildcards and regular expressions are only available for the _simple-find-part_ of the query, i. e. no wildcards/regexps for filters.
+`FIND en*` returns any record which has a name beginning with _en_.
 
-### Simple COUNT Query
+Regular expressions must be surrounded by _<<_ and _>>_:
 
-This query counts entities which have certain properties.
+`FIND <<e[aemn]{2,5}>>`
 
-`COUNT ename`
-will return the number of entities which have the name _ename_ and all their children.
+`FIND <<[cC]amera_[0-9]*>>`
 
-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`. 
+*TODO*:
+Describe escape sequences like `\\\\ `, `\*`, `\<<` and `\>>`.
 
-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.
+Currently, wildcards and regular expressions are only available for the _simple-find-part_ of the
+query, i.e. not for property-operator-value filters (see below).
+
+## Simple COUNT Query
+
+COUNT queries count entities which have certain properties.
+
+`COUNT ... rname ...`
+
+will return the number of records which have the name _rname_ and all record
+children of any entity with that name.
+
+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_.
+The following queries are equivalent and will restrict the result set to records which have a property named _pname1_ that has a value _val1_.
 
 `FIND ename.pname1=val1`
 
@@ -53,31 +70,33 @@ The following queries are equivalent and will restrict the result set to entitie
 
 `FIND ename WHICH HAS A pname1=val1`
 
-Again, the resultset can be restricted to records:
+Again, the resultset can be restricted to any other entity role as well:
 
-`FIND RECORD ename WHICH HAS A pname1=val1`
+`FIND RECORDTYPE 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: 
+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 ename WHICH HAS A pname1 LIKE va*`
 
-`FIND RECORD ename WHICH HAS A pname1 LIKE va*1`
+`FIND ename WHICH HAS A pname1 LIKE va*1`
 
-`FIND RECORD ename WHICH HAS A pname1 LIKE *al1`
+`FIND ename WHICH HAS A pname1 LIKE *al1`
 
-_Note:_ The _LIKE_ operator is will only produce expectable results with text properties.
+_Note:_ The _LIKE_ operator will only produce expectable results with text properties.
 
 #### Special Case: References
 
-In general a reference can be addressed just like a POV filter. So 
+In general a reference can be addressed just like a POV filter. So
 
-`FIND ename1.pname1=ename2`
+`FIND ename1 WITH 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:
+will also return any record 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`
 
@@ -88,12 +107,12 @@ The query looks like this:
 
 `FIND ename1 WHICH HAS A pname1->ename2`
 
-#### Time Special Case: DateTime
+#### 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). 
+##### `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.
 
@@ -103,11 +122,11 @@ Examples:
 * `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. 
+* `2015-04=2015-04` is true.
 
-##### `d1!=d2`: Intransitive, symmetric relation. 
-* ''True'' iff `d1=d2` is false. 
-* ''False'' iff `d1=d2` is true. 
+##### `d1!=d2`: Intransitive, symmetric relation.
+* ''True'' iff `d1=d2` is false.
+* ''False'' iff `d1=d2` is true.
 * ''Undefined'' otherwise.
 
 Examples:
@@ -116,7 +135,7 @@ Examples:
 * `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. 
+* `2015-04!=2015-04` is false
 
 ##### `d1>d2`: Transitive, non-symmetric relation.
 Semantics depend on the flavors of d1 and d2. If both are... 
@@ -175,7 +194,7 @@ Examples:
 * `2015 IN 2015-01-01` is false.
 * `2015-01-01 IN 2015-01-01T20:15:30` is false.
 
-##### `d1 NOT IN d2`: Non-symmetric relation.
+##### `d1 NOT IN d2`: Transitive, non-symmetric relation.
 Semantics depend on the flavors of d1 and d2. If both are... 
 ###### [SemiCompleteDateTime](specification/Datatype.html#datetime)
 * ''True'' iff `d1.ILB IN d2.ILB` is false.
@@ -193,7 +212,7 @@ These semantics follow a three-valued logic with ''true'', ''false'' and ''undef
 
 #### 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_.
+One doesn't have to specify the property or the value at all. The following query filters the result set for records which have any property with a value greater than _val1_.
 
 `FIND ename WHICH HAS A PROPERTY > val1`
 
@@ -213,7 +232,7 @@ And for references...
 `FIND ename1.->ename2`
 
 
-The following query returns entities which have a _pname1_ property with any value.
+The following query returns records which have a _pname1_ property with any value.
 
 `FIND ename WHICH HAS A PROPERTY pname1`
 
@@ -227,22 +246,17 @@ The following query returns entities which have a _pname1_ property with any val
 
 ### TransactionFilter
 
+`FIND ename WHICH (sugar|negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED) (by_clause time_clause?| time_clause by_clause?)`
+
 *Definition*
 
- sugar:: `HAS BEEN` | `HAVE BEEN` | `HAD BEEN` | `WAS` | `IS` | 
+ 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`
+ 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)`
-
- date:: A date string of the form `YYYY-MM-DD`
-
- datetime:: A datetime string of the form `YYYY-MM-DD hh:mm:ss`
-
- time_clause:: `ON ($date|$datetime) ` Here is plenty of room for more syntactic sugar, e.g. a `TODAY` keyword, and more funcionality, e.g. ranges.  
-
-`FIND ename WHICH ($sugar|$negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED) (by_clause time_clause?| time_clause by_clause?)`
+ datetime:: A datetime string of the form `YYYY[-MM[-DD(T| )[hh[:mm[:ss[.nnn][(+|-)zzzz]]]]]]` or `TODAY`.
+ time_clause:: `[AT|ON|IN|BEFORE|AFTER|UNTIL|SINCE] (datetime) `
 
 *Examples*
 
@@ -256,8 +270,9 @@ The following query returns entities which have a _pname1_ property with any val
 
 `FIND ename WHICH HAS BEEN INSERTED BY erwin`
 
-`FIND ename . CREATED BY erwin ON `
+`FIND ename WHICH HAS BEEN INSERTED SINCE 2021-04`
 
+Note that `SINCE` and `UNTIL` are inclusive, while `BEFORE` and `AFTER` are not.
 
 
 ### File Location
@@ -297,7 +312,7 @@ Find any file in a directory which begins with `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_. 
+The back reference filters for entities that are referenced by another entity. The following query returns records 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`
@@ -363,7 +378,7 @@ Any result set can be filtered by logically combining POV filters or back refere
 
 In contrast to `FIND` queries, which always return the complete entity, there are `SELECT` queries which only return the entity with only those properties which are specified in the query. The syntax is very similar to `FIND` queries - just replace the `FIND` by `SELECT <comma separated list of selectors> FROM`:
 
-`SELECT p1, p2, p3 FROM Record ename`
+`SELECT p1, p2, p3 FROM ename`
 
 However, the `SELECT` query can also return properties of referenced entities and thereby are a means of joining entities together and return a custom view or projection:
 
@@ -398,6 +413,15 @@ 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.
 
+## Configuration
+
+In CaosDB Server implementations before version 0.9.0, the `FIND ename` query
+would return any entity with that name and all children, regardless of the
+entity's role. Basically, `FIND ename` *was* equivalent to `FIND ENTITY ename`.
+Since 0.9.0 the default behavior has changed and now `FIND ename` is equivalent
+to `FIND RECORD ename`. This default is, however, configurable via the
+`FIND_QUERY_DEFAULT_ROLE` server property. See [Server Configuration](./administration/configuration.rst).
+
 ## Future
 
 * Additional versioning queries:
@@ -408,4 +432,3 @@ Since Caosdb 0.2 entities are optionally version controlled. The query language
     WITH ...`.
   * Find deleted entities: `FIND ename WHICH WAS DELETED (BY ME | ON 2014-12-24)`
 * *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/conf.py b/src/doc/conf.py
index c7ff480a90f6d8d7aca14bd61bfcc4afc8d79c6b..700a009370e0925a209efe66da04f18324beea69 100644
--- a/src/doc/conf.py
+++ b/src/doc/conf.py
@@ -17,6 +17,7 @@
 # sys.path.insert(0, os.path.abspath('../caosdb'))
 
 import sphinx_rtd_theme
+from os.path import dirname, abspath
 
 # -- Project information -----------------------------------------------------
 
@@ -48,6 +49,7 @@ extensions = [
     "sphinx.ext.autosectionlabel",  # Allow reference sections using its title
     "sphinx_rtd_theme",
     "sphinxcontrib.plantuml",  # PlantUML diagrams
+    "sphinx_a4doc", # antl4
 ]
 
 # Add any paths that contain templates here, relative to this directory.
@@ -223,3 +225,7 @@ autodoc_default_options = {
 # -- Options for autosectionlabel --------------------------------------------
 
 autosectionlabel_prefix_document = True
+
+# -- Options for sphinx_a4doc ------------------------------------------------
+
+a4_base_path = abspath(dirname(__file__) + '/../main/java/org/caosdb/server/query')
diff --git a/src/doc/index.rst b/src/doc/index.rst
index b5ce9f3235277b613b357dca5dfc3334d80aeb3f..1a5e4134ef7f934ff5018cc8847603f1165ab16e 100644
--- a/src/doc/index.rst
+++ b/src/doc/index.rst
@@ -18,7 +18,7 @@ Welcome to caosdb-server's documentation!
    Changelog <CHANGELOG>
    specification/index.rst
    Glossary
-   API documentation<_apidoc/packages>
+   Server Internals<_apidoc/packages>
 
 Welcome to the CaosDB, the flexible semantic data management toolkit!
 
diff --git a/src/doc/query-syntax.rst b/src/doc/query-syntax.rst
new file mode 100644
index 0000000000000000000000000000000000000000..00df440fb9cf2a175e371c3038b164643d89a84a
--- /dev/null
+++ b/src/doc/query-syntax.rst
@@ -0,0 +1,15 @@
+CaosDB Query Language Syntax
+============================
+
+This is the documentation of the CaosDB Query Language Syntax. The
+authoritative specification of the syntax is the ANTLR4 grammar you can find in
+the source code:
+`CQLParser.g4 <https://gitlab.com/caosdb/caosdb-server/-/blob/main/src/main/java/org/caosdb/server/query/CQLParser.g4>`__
+and
+`CQLLexer.g4 <https://https://gitlab.com/caosdb/caosdb-server/-/blob/main/src/main/java/org/caosdb/server/query/CQLLexer.g4>`__
+
+See examples in :doc:`Query Language<CaosDB-Query-Language>`.
+
+.. a4:autogrammar:: CQLParser.g4
+
+.. a4:autogrammar:: CQLLexer.g4
diff --git a/src/main/java/org/caosdb/server/ServerProperties.java b/src/main/java/org/caosdb/server/ServerProperties.java
index 87f5b22d55dfc11a8ae0158df85fa69d2b6cdc9d..d58250242f691498288d765dd252986eda7fb09f 100644
--- a/src/main/java/org/caosdb/server/ServerProperties.java
+++ b/src/main/java/org/caosdb/server/ServerProperties.java
@@ -31,6 +31,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Properties;
+import org.caosdb.server.query.Query;
 import org.caosdb.server.utils.AbstractObservable;
 import org.caosdb.server.utils.Observable;
 import org.caosdb.server.utils.Observer;
@@ -114,6 +115,7 @@ public class ServerProperties extends Properties implements Observable {
 
   public static final String KEY_QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS =
       "QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS";
+  public static final String KEY_FIND_QUERY_DEFAULT_ROLE = "FIND_QUERY_DEFAULT_ROLE";
 
   public static final String KEY_SERVER_NAME = "SERVER_NAME";
 
@@ -199,9 +201,25 @@ public class ServerProperties extends Properties implements Observable {
         logger.info(name + "=" + val);
       }
     }
+
+    sanityCheck(serverProperties);
+
     return serverProperties;
   }
 
+  private static void sanityCheck(ServerProperties serverProperties) {
+    try {
+      Query.Role.valueOf(serverProperties.getProperty(KEY_FIND_QUERY_DEFAULT_ROLE));
+    } catch (IllegalArgumentException e) {
+      logger.error(
+          "Unsupported value for server property "
+              + KEY_FIND_QUERY_DEFAULT_ROLE
+              + ": "
+              + serverProperties.getProperty(KEY_FIND_QUERY_DEFAULT_ROLE));
+      System.exit(1);
+    }
+  }
+
   private static void loadConfigFile(final Properties serverProperties, final File confFile)
       throws IOException {
     if (confFile.exists() && confFile.isFile()) {
diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4
index eba7f773a7e83360671d80f0bc3dde54e72ff625..518d1628b16ab9d0d66816b88e9b9115b765b6d4 100644
--- a/src/main/java/org/caosdb/server/query/CQLLexer.g4
+++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4
@@ -21,93 +21,115 @@
  */
 lexer grammar CQLLexer;
 
+/** */
 AS_A:
     [Aa][Ss] (WHITE_SPACE_f? A)? WHITE_SPACE_f?
 ;
 
+/** */
 IS_REFERENCED:
     (IS_f WHITE_SPACE_f?)? [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 BY:
     [Bb][Yy] WHITE_SPACE_f?
 ;
 
+/** */
 fragment
 OF_f:
     [Oo][Ff]
 ;
 
+/** */
 fragment
 ANY_f:
     [Aa][Nn][Yy]
 ;
 
+/** */
 fragment
 VERSION_f:
     [Vv][Ee][Rr][Ss][Ii][Oo][Nn]
 ;
 
+/** */
 ANY_VERSION_OF:
     (ANY_f WHITE_SPACE_f VERSION_f WHITE_SPACE_f OF_f) WHITE_SPACE_f?
 ;
 
+/** */
 SELECT:
     [Ss][Ee][Ll][Ee][Cc][Tt] WHITE_SPACE_f? -> pushMode(SELECT_MODE)
 ;
 
+/** */
 INSERTED:
     [Ii][Nn][Ss][Ee][Rr][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 CREATED:
     [Cc][Rr][Ee][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 UPDATED:
     [Uu][Pp][Dd][Aa][Tt][Ee][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 ON:
     [Oo][Nn] WHITE_SPACE_f?
 ;
 
+/** */
 IN:
     [Ii][Nn] WHITE_SPACE_f?
 ;
 
+/** */
 AFTER:
     [Aa][Ff][Tt][Ee][Rr] WHITE_SPACE_f?
 ;
 
+/** */
 BEFORE:
     [Bb][Ee][Ff][Oo][Rr][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 UNTIL:
     [Uu][Nn][Tt][Ii][Ll] WHITE_SPACE_f?
 ;
 
+/** */
 SINCE:
     [Ss][Ii][Nn][Cc][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 IS_STORED_AT:
     (IS_f WHITE_SPACE_f?)? [Ss][Tt][Oo][Rr][Ee][Dd] (WHITE_SPACE_f? AT)? WHITE_SPACE_f?
 ;
 
+/** */
 AT:
     [Aa][Tt] WHITE_SPACE_f?
 ;
 
+/** */
 FIND:
     [Ff][Ii][Nn][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 COUNT:
     [Cc][Oo][Uu][Nn][Tt] WHITE_SPACE_f?
 ;
 
+/** */
 AND:
     (
         (
@@ -117,6 +139,7 @@ AND:
     ) WHITE_SPACE_f?
 ;
 
+/** */
 OR:
     (
         (
@@ -126,22 +149,27 @@ OR:
     ) WHITE_SPACE_f?
 ;
 
+/** */
 LPAREN:
     '(' WHITE_SPACE_f?
 ;
 
+/** */
 RPAREN:
     ')' WHITE_SPACE_f?
 ;
 
+/** */
 SINGLE_QUOTE_START:
     '\'' -> pushMode(SINGLE_QUOTE_MODE)
 ;
 
+/** */
 DOUBLE_QUOTE_START:
     '"' -> pushMode(DOUBLE_QUOTE_MODE)
 ;
 
+/** */
 OPERATOR:
     '='
     | '<'
@@ -153,115 +181,138 @@ OPERATOR:
     | [Rr][Ee][Ff][Ee][Rr][Ee][Nn][Cc][Ee]([Ss]|WHITE_SPACE_f? [Tt][Oo]) (WHITE_SPACE_f? A {_input.LA(1) == ' '}?)? {setText("->");}
 ;
 
+/** */
 LIKE:
     [Ll][Ii][Kk][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 IS_NULL:
     IS_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
 ;
 
+/** */
 IS_NOT_NULL:
     IS_f WHITE_SPACE_f NOT_f WHITE_SPACE_f NULL_f WHITE_SPACE_f?
 ;
 
+/** */
 fragment
 NULL_f:
     [Nn][Uu][Ll][Ll]
 ;
 
+/** */
 fragment
 DOES_f:
     [Dd][Oo][Ee][Ss]
 ;
 
+/** */
 fragment
 NOT_f:
     [Nn][Oo][Tt]
 ;
 
+/** */
 fragment
 DOESNT_f:
     DOES_f WHITE_SPACE_f? NOT_f
     | DOES_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 ISNT_f:
     IS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 WERE_f:
     [Ww][Ee][Rr][Ee]
 ;
 
+/** */
 fragment
 WERENT_f:
     WERE_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 HAVENT_f:
     HAVE_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 HADNT_f:
     HAD_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 HAD_f:
     [Hh][Aa][Dd]
 ;
 
+/** */
 fragment
 HAVE_f:
     [Hh][Aa][Vv][Ee]
 ;
 
+/** */
 fragment
 HAS_f:
     [Hh][Aa][Ss]
 ;
 
+/** */
 fragment
 HASNT_f:
     HAS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 BEEN_f:
     [Bb][Ee][Ee][Nn]
 ;
 
+/** */
 fragment
 HAVE_A_f:
     HAVE_f (WHITE_SPACE? A)?
 ;
 
+/** */
 fragment
 DO_f:
     [Dd][Oo]
 ;
 
+/** */
 fragment
 DONT_f:
     DO_f NOT_f
     | DO_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 fragment
 WAS_f:
     [Ww][Aa][Ss]
 ;
 
+/** */
 fragment
 WASNT_f:
     WAS_f [Nn] SINGLE_QUOTE [Tt]
 ;
 
+/** */
 NEGATION:
     (
         '!'
@@ -277,46 +328,57 @@ NEGATION:
     ) WHITE_SPACE_f?
 ;
 
+/** */
+WITH_A:
+    [Ww][Ii][Tt][Hh] (WHITE_SPACE_f? A)? WHITE_SPACE_f?
+;
+
+/** */
 THE:
     [Tt][Hh][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 GREATEST:
     [Gg][Rr][Ee][Aa][Tt][Ee][Ss][Tt] WHITE_SPACE_f?
 ;
 
+/** */
 SMALLEST:
     [Ss][Mm][Aa][Ll][Ll][Ee][Ss][Tt] WHITE_SPACE_f?
 ;
 
+/** */
 A:
     [Aa][Nn]? WHITE_SPACE_f?
 ;
 
+/** */
 ME:
     [Mm][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 SOMEONE:
     [Ss][Oo][Mm][Ee][Oo][Nn][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 ELSE:
     [Ee][Ll][Ss][Ee] WHITE_SPACE_f?
 ;
 
-WITH_A:
-    [Ww][Ii][Tt][Hh] (WHITE_SPACE_f? A)? WHITE_SPACE_f?
-;
-
+/** */
 WHERE:
     [Ww][Hh][Ee][Rr][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 WHICH:
     [Ww][Hh][Ii][Cc][Hh] WHITE_SPACE_f?
 ;
 
+/** */
 HAS_A:
     (
         (HAS_f | HAD_f | HAVE_f | WERE_f | WAS_f | IS_f)
@@ -327,236 +389,293 @@ HAS_A:
     ) WHITE_SPACE_f?
 ;
 
+/** */
 PROPERTY:
     [Pp][Rr][Oo][Pp][Ee][Rr][Tt]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f?
 ;
 
+/** */
 RECORDTYPE:
     [Rr][Ee][Cc][Oo][Rr][Dd][Tt][Yy][Pp][Ee]([Ss])? WHITE_SPACE_f?
 ;
 
+/** */
 RECORD:
     [Rr][Ee][Cc][Oo][Rr][Dd]([Ss])? WHITE_SPACE_f?
 ;
 
+/** */
 FILE:
     [Ff][Ii][Ll][Ee]([Ss])? WHITE_SPACE_f?
 ;
 
+/** */
 ENTITY:
     [Ee][Nn][Tt][Ii][Tt]([Yy]|[Ii][Ee][Ss]) WHITE_SPACE_f?
 ;
 
+/** */
 QUERYTEMPLATE:
     [Qq][Uu][Ee][Rr][yY][Tt][Ee][Mm][Pp][Ll][Aa][Tt][Ee] WHITE_SPACE_f?
 ;
 
+/** */
 fragment
 IS_f:
     [Ii][Ss]
 ;
 
+/** */
 fragment
 WHITE_SPACE_f:
     [ \t\n\r]+
 ;
 
+/** */
 WHITE_SPACE:
     [ \t\n\r]+
 ;
 
+/** */
 fragment
 DOUBLE_QUOTE:
     '"'
 ;
 
+/** */
 fragment
 SINGLE_QUOTE:
     '\''
 ;
 
+/** */
 REGEXP_MARKER:
     '#'
 ;
 
+/** */
 REGEXP_BEGIN:
     '<<'
 ;
 
+/** */
 REGEXP_END:
     '>>'
 ;
 
+/** */
 ID:
     [Ii][Dd] WHITE_SPACE_f?
 ;
 
+/** */
 SLASH:
     '/'
 ;
 
+/** */
 STAR:
     '*'
 ;
 
+/** */
 DOT:
     '.'
 ;
 
+/** */
 QMARK:
     '?' WHITE_SPACE_f?
 ;
 
+/** */
 BUT:
     [Bb][Uu][Tt] WHITE_SPACE_f?
 ;
 
+/** */
 ESC_REGEXP_END:
     ESC_MARKER
     '>>' WHITE_SPACE_f?
 ;
 
+/** */
 ESC_STAR:
     ESC_MARKER
     '*' WHITE_SPACE_f?
 ;
 
+/** */
 ESC_BS:
     ESC_MARKER
     '\\' WHITE_SPACE_f?
 ;
 
+/** */
 fragment
 ESC_MARKER:
     '\\'
 ;
 
+/** */
 TODAY:
     [Tt][Oo][Dd][Aa][Yy] WHITE_SPACE_f?
 ;
 
+/** */
 HYPHEN:
     '-'
 ;
 
+/** */
 COLON:
     ':'
 ;
 
+/** */
 NUM:
     NUM_f
     | DOT NUM_f
     | NUM_f DOT NUM_f
 ;
 
+/** */
 NUM_f:
     ('0'..'9')+
 ;
 
+/** */
 TXT:
     ('a'..'z' | 'A'..'Z' | '0'..'9' | '_' | '-' {_input.LA(1) != '>'}? | '+' | '&' | ';' | ',' | '$' | ':' | '%' | '^' | '~' {_input.LA(1) != '='}? | '`' | '´' | 'ö' | 'ä' | 'ß' | 'ü' | 'Ö' | 'Ä' | 'Ü' | '@' | '[' | ']' | '{' | '}' )+
 ;
 
+/** */
 UNKNOWN_CHAR: . ;
 
+/** */
 mode SINGLE_QUOTE_MODE;
 
+    /** */
     SINGLE_QUOTE_ESCAPED_CHAR:
         ESC_MARKER
         ( '\'' | '\\' | '*' )
     ;
 
+    /** */
     SINGLE_QUOTE_END:
         '\'' -> mode(DEFAULT_MODE)
     ;
 
+    /** */
     SINGLE_QUOTE_STAR:
         '*'
     ;
 
+    /** */
     SINGLE_QUOTE_ANY_CHAR:
         ~('\''|'\\'|'*')+
     ;
 
+/** */
 mode DOUBLE_QUOTE_MODE;
 
+    /** */
     DOUBLE_QUOTE_ESCAPED_CHAR:
         ESC_MARKER
         ( '"' | '\\' | '*' )
     ;
 
+    /** */
     DOUBLE_QUOTE_END:
         '"' -> mode(DEFAULT_MODE)
     ;
 
+    /** */
     DOUBLE_QUOTE_STAR:
         '*'
     ;
 
+    /** */
     DOUBLE_QUOTE_ANY_CHAR:
         ~('"'|'\\'|'*')+
     ;
 
 
+/** */
 mode SELECT_DOUBLE_QUOTED;
 
+    /** */
     SELECT_DOUBLE_QUOTE_ESCAPED:
         ESC_MARKER
         '"'
     ;
 
+    /** */
     SELECT_DOUBLE_QUOTE_END:
         '"' WHITE_SPACE_f? {setText("");} -> mode(SELECT_MODE)
     ;
 
+    /** */
     SELECT_DOUBLE_QUOTE_TXT:
         .
     ;
 
+/** */
 mode SELECT_SINGLE_QUOTED;
 
+    /** */
     SELECT_SINGLE_QUOTE_ESCAPED:
         ESC_MARKER
         '\''
     ;
 
+    /** */
     SELECT_SINGLE_QUOTE_END:
         '\'' WHITE_SPACE_f? {setText("");} -> mode(SELECT_MODE)
     ;
 
+    /** */
     SELECT_SINGLE_QUOTE_TXT:
         .
     ;
 
+/** */
 mode SELECT_MODE;
 
+    /** */
     FROM:
          [Ff][Rr][Oo][Mm]([ \t\n\r])* -> mode(DEFAULT_MODE)
     ;
 
+    /** */
     SELECT_ESCAPED:
         ESC_MARKER
         ( '"' | '\\' | '\'' | ',' | '.' ) {setText(getText().substring(1));}
     ;
 
+    /** */
     SELECT_DOT:
         '.'
         WHITE_SPACE_f?
     ;
 
+    /** */
     SELECT_DOUBLE_QUOTE:
         '"' {setText("");} -> mode(SELECT_DOUBLE_QUOTED)
     ;
 
+    /** */
     SELECT_SINGLE_QUOTE:
         '\'' {setText("");} -> mode(SELECT_SINGLE_QUOTED)
     ;
 
+    /** */
     SELECT_COMMA:
         ','
         WHITE_SPACE_f?
     ;
 
+    /** */
     SELECTOR_TXT:
         .
     ;
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index cf16baef48b5be4af25522fcf9075710a0a835a3..bcb4645ad7019d61f933793989e5d0afa5521f09 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -29,6 +29,9 @@ options { tokenVocab = CQLLexer; }
     import java.util.List;
 }
 
+/**
+ * This is the root of the CQL grammar.
+ */
 cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r, EntityFilterInterface filter, VersionFilter v]
     @init{
         $s = null;
@@ -56,6 +59,9 @@ cq returns [Query.Type t, List<Query.Selection> s, Query.Pattern e, Query.Role r
     EOF
 ;
 
+/**
+ * For versioned queries.
+ */
 version returns [VersionFilter v]
     @init{
         $v = null;
@@ -64,6 +70,9 @@ version returns [VersionFilter v]
     ANY_VERSION_OF {$v = VersionFilter.ANY_VERSION;}
 ;
 
+/**
+ * The comma-separated list of selected (sub)properties of a SELECT query.
+ */
 prop_sel returns [List<Query.Selection> s]
     @init{
         $s = new LinkedList<Query.Selection>();
@@ -73,6 +82,9 @@ prop_sel returns [List<Query.Selection> s]
     (SELECT_COMMA prop_subsel {$s.add($prop_subsel.sub);})*
 ;
 
+/**
+ * The (sub-)selections (e.g. geolocation.longitude) of a SELECT query.
+ */
 prop_subsel returns [Query.Selection sub]:
     selector_txt {$sub = new Query.Selection($selector_txt.text);}
     (
@@ -80,6 +92,9 @@ prop_subsel returns [Query.Selection sub]:
     )?
 ;
 
+/**
+ * Rule for the allowed characters of a prop_subsel.
+ */
 selector_txt:
     (
         SELECT_DOUBLE_QUOTE
@@ -94,6 +109,9 @@ selector_txt:
     ( SELECTOR_TXT | SELECT_ESCAPED )+
 ;
 
+/**
+ * The entity role.
+ */
 role returns [Query.Role r]:
     RECORDTYPE {$r = Query.Role.RECORDTYPE;}
     | RECORD {$r = Query.Role.RECORD;}
@@ -103,6 +121,9 @@ role returns [Query.Role r]:
     | ENTITY {$r = Query.Role.ENTITY;}
 ;
 
+/**
+ * The filters of a FIND, SELECT, or COUNT query.
+ */
 entity_filter returns [EntityFilterInterface filter]
     @init{
         $filter = null;
@@ -126,6 +147,9 @@ entity_filter returns [EntityFilterInterface filter]
     )?
 ;
 
+/**
+ * WHICH keyword and syntactic sugar.
+ */
 which_exp:
     WHICH (HAS_A (PROPERTY)?)?
     | HAS_A (PROPERTY)?
@@ -134,6 +158,9 @@ which_exp:
     | DOT WHITE_SPACE?
 ;
 
+/**
+ * Collection of different filters.
+ */
 filter_expression returns [EntityFilterInterface efi]
 :
     backreference (subproperty {((Backreference) $backreference.ref).setSubProperty($subproperty.subp);})? {$efi = $backreference.ref;}
@@ -145,6 +172,9 @@ filter_expression returns [EntityFilterInterface efi]
     | negation {$efi=$negation.n;}
 ;
 
+/**
+ * ID Filter (e.g. id > 10123).
+ */
 idfilter returns [IDFilter filter] locals [String o, String v, String a]
 @init{
     $a = null;
@@ -164,6 +194,9 @@ idfilter returns [IDFilter filter] locals [String o, String v, String a]
     )?
 ;
 
+/**
+ * Transaction filter (e.g INSERTED BY ME).
+ */
 transaction returns [TransactionFilter filter] locals [String type, TransactionFilter.Transactor user, String time, String time_op]
 @init{
     $time = null;
@@ -186,6 +219,9 @@ transaction returns [TransactionFilter filter] locals [String type, TransactionF
     )
 ;
 
+/**
+ * The transactor (e.g. "user1", or "ME").
+ */
 transactor returns [TransactionFilter.Transactor t]
 :
     BY
@@ -200,6 +236,9 @@ transactor returns [TransactionFilter.Transactor t]
     )
 ;
 
+/**
+ * A user name or a user name pattern.
+ */
 username returns [Query.Pattern ep] locals [int type]
 @init{
     $type = Query.Pattern.TYPE_NORMAL;
@@ -211,6 +250,9 @@ username returns [Query.Pattern ep] locals [int type]
     ( STAR {$type = Query.Pattern.TYPE_LIKE;} | ~(STAR | WHITE_SPACE) )+
 ;
 
+/**
+ * Time or timeframe of a transaction (for the transaction filter).
+ */
 transaction_time returns [String tqp, String op]
 @init {
      $op = "(";
@@ -232,9 +274,11 @@ transaction_time returns [String tqp, String op]
     )
 ;
 
-/*
-* not fully compliant with iso 8601 (TODO)
-*/
+/**
+ * A date time or a fragment of a date time.
+ *
+ * Not fully compliant with iso 8601 (TODO).
+ */
 datetime
 :
     NUM // year
@@ -259,6 +303,10 @@ datetime
     )?
 ;
 
+/**
+ * The property-operator-value filter (e.g. temperator > 240°C) and
+ * (optionally) subfilters.
+ */
 pov returns [POV filter] locals [Query.Pattern p, String o, String v, String a]
     @init{
         $p = null;
@@ -300,6 +348,9 @@ pov returns [POV filter] locals [Query.Pattern p, String o, String v, String a]
 ;
 
 
+/**
+ * Wrapper for a filter on referenced entities.
+ */
 subproperty returns [SubProperty subp]
 @init{
     $subp = null;
@@ -308,6 +359,9 @@ subproperty returns [SubProperty subp]
     subproperty_filter {$subp = new SubProperty($subproperty_filter.filter);}
 ;
 
+/**
+ * The actual filter on referenced entities.
+ */
 subproperty_filter returns [EntityFilterInterface filter]
     @init{
         $filter = null;
@@ -329,7 +383,9 @@ subproperty_filter returns [EntityFilterInterface filter]
     )?
 ;
 
-
+/**
+ * The backreference filter (e.g. REFERENCED BY ...).
+ */
 backreference returns [Backreference ref] locals [Query.Pattern e, Query.Pattern p]
     @init{
         $e = null;
@@ -349,6 +405,9 @@ backreference returns [Backreference ref] locals [Query.Pattern e, Query.Pattern
     WHITE_SPACE?
 ;
 
+/**
+ * Stored-at filter for the path of files.
+ */
 storedat returns [StoredAt filter] locals [String loc]
     @init{
         $loc = null;
@@ -362,6 +421,9 @@ storedat returns [StoredAt filter] locals [String loc]
     WHITE_SPACE?
 ;
 
+/**
+ * Combine other filters with a logical AND.
+ */
 conjunction returns [Conjunction c]
     @init{
         $c = new Conjunction();
@@ -399,6 +461,9 @@ conjunction returns [Conjunction c]
     )+
 ;
 
+/**
+ * Combine other filter with a logical OR.
+ */
 disjunction returns [Disjunction d]
     @init{
         $d = new Disjunction();
@@ -435,6 +500,9 @@ disjunction returns [Disjunction d]
     )+
 ;
 
+/**
+ * Negation of another filter.
+ */
 negation returns [Negation n]
     @init{
     }
@@ -454,6 +522,9 @@ negation returns [Negation n]
     )
 ;
 
+/**
+ * An entity's full name, id, or a pattern for a name.
+ */
 entity returns [Query.Pattern ep]
 :
     regexp_pattern {$ep = $regexp_pattern.ep;}
@@ -464,6 +535,10 @@ entity returns [Query.Pattern ep]
     | ( ~(ENTITY |WHITE_SPACE | DOT) )+ {$ep = new Query.Pattern((String) $text.trim(), Query.Pattern.TYPE_NORMAL);}
 ;
 
+
+/**
+ * A regexp pattern.
+ */
 regexp_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
     @init{
         $sb = new StringBuffer();
@@ -474,6 +549,9 @@ regexp_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
     REGEXP_END {$ep = new Query.Pattern((String) $sb.toString(), Query.Pattern.TYPE_REGEXP);}
 ;
 
+/**
+ * A like pattern (e.g. "*oxide").
+ */
 like_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
     @init{
         $sb = new StringBuffer();
@@ -485,6 +563,9 @@ like_pattern returns [Query.Pattern ep] locals [StringBuffer sb]
     {$ep = new Query.Pattern((String) $text, Query.Pattern.TYPE_LIKE);}
 ;
 
+/**
+ * A Property's name, id or a pattern of a name.
+ */
 property returns [Query.Pattern pp, String agg]locals [StringBuffer sb]
     @init{
         $sb = new StringBuffer();
@@ -502,6 +583,9 @@ property returns [Query.Pattern pp, String agg]locals [StringBuffer sb]
     WHITE_SPACE?
 ;
 
+/**
+ * Expression for minumum or maximum.
+ */
 minmax returns [String agg]
 :
     (THE?? (
@@ -510,6 +594,9 @@ minmax returns [String agg]
     ))
 ;
 
+/**
+ * A property value.
+ */
 value returns [String str]
 :
     number_with_unit {$str = $number_with_unit.text;}
@@ -518,6 +605,9 @@ value returns [String str]
     WHITE_SPACE?
 ;
 
+/**
+ * A number with a unit (e.g. 20m).
+ */
 number_with_unit
 :
     HYPHEN??
@@ -525,6 +615,9 @@ number_with_unit
     (WHITE_SPACE?? unit)?
 ;
 
+/**
+ * A unit from a physical system of units or any other kind of system.
+ */
 unit
 :
     (~(WHITE_SPACE | WHICH | HAS_A | WITH_A | WHERE | DOT | AND | OR | RPAREN ))
@@ -533,6 +626,9 @@ unit
     NUM SLASH (~(WHITE_SPACE))+
 ;
 
+/**
+ * A files path.
+ */
 location returns [String str]
 :
     atom {$str = $atom.ep.str;}
@@ -540,6 +636,9 @@ location returns [String str]
     (~WHITE_SPACE)+ {$str = $text; }
 ;
 
+/**
+ * An atomic string or pattern.
+ */
 atom returns [Query.Pattern ep]
 :
     double_quoted {$ep = $double_quoted.ep;}
@@ -547,6 +646,9 @@ atom returns [Query.Pattern ep]
     | (~(WHITE_SPACE | DOT | RPAREN | LPAREN ))+ {$ep = new Query.Pattern($text, Query.Pattern.TYPE_NORMAL);}
 ;
 
+/**
+ * A single-quoted string or pattern.
+ */
 single_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternType]
     @init{
         $sb = new StringBuffer();
@@ -567,6 +669,9 @@ single_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternTyp
     SINGLE_QUOTE_END
 ;
 
+/**
+ * A double-quoted string or pattern.
+ */
 double_quoted returns [Query.Pattern ep] locals [StringBuffer sb, int patternType]
     @init{
         $sb = new StringBuffer();
diff --git a/src/main/java/org/caosdb/server/query/Query.java b/src/main/java/org/caosdb/server/query/Query.java
index 88da39bce24f1cabf651eebf1532cab14bddb82c..c4a458630456d057ccddf26c48def0e72ccf3789 100644
--- a/src/main/java/org/caosdb/server/query/Query.java
+++ b/src/main/java/org/caosdb/server/query/Query.java
@@ -55,6 +55,7 @@ import org.caosdb.server.caching.Cache;
 import org.caosdb.server.database.access.Access;
 import org.caosdb.server.database.backend.implementation.MySQL.ConnectionException;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLHelper;
+import org.caosdb.server.database.backend.transaction.RetrieveFullEntityTransaction;
 import org.caosdb.server.database.backend.transaction.RetrieveSparseEntity;
 import org.caosdb.server.database.misc.DBHelper;
 import org.caosdb.server.database.misc.TransactionBenchmark;
@@ -596,14 +597,27 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
    * </ol>
    */
   public void optimize() {
-    // basic optimization
+    // "FIND Person" is interpreted as "FIND RECORD Person"
+    if (this.role == null) {
+      try {
+        this.role =
+            Role.valueOf(
+                CaosDBServer.getServerProperty(ServerProperties.KEY_FIND_QUERY_DEFAULT_ROLE));
+      } catch (IllegalArgumentException e) {
+        addError(
+            "Unsupportet value of the server property "
+                + ServerProperties.KEY_FIND_QUERY_DEFAULT_ROLE
+                + ": "
+                + CaosDBServer.getServerProperty(ServerProperties.KEY_FIND_QUERY_DEFAULT_ROLE));
+        throw new UnsupportedOperationException(e);
+      }
+    }
+
+    // "FIND *" is interpreted as "FIND RECORD", "FIND <ROLE> *" as "FIND <ROLE>"
     if (this.entity != null
         && this.entity.type == Pattern.TYPE_LIKE
         && this.entity.str.equals("*")) {
       this.entity = null;
-      if (this.role == null) {
-        this.role = Role.ENTITY;
-      }
     }
   }
 
@@ -830,6 +844,10 @@ public class Query implements QueryInterface, ToElementable, TransactionInterfac
     this.messages.add(new Message(MessageType.Warning, MessageCode.MESSAGE_CODE_UNKNOWN, w));
   }
 
+  private void addError(final String e) {
+    this.messages.add(new Message(MessageType.Error, MessageCode.MESSAGE_CODE_UNKNOWN, e));
+  }
+
   private void cleanUp() {
     if (getConnection() != null) {
       ResultSet rs = null;
diff --git a/src/test/docker/Dockerfile b/src/test/docker/Dockerfile
index f3c8b98fdab34400cccfc11f0c9211eeca37f845..ec670b7d4b43709054e9794f501aea111a059a0a 100644
--- a/src/test/docker/Dockerfile
+++ b/src/test/docker/Dockerfile
@@ -5,8 +5,8 @@ RUN apt-get update && \
     plantuml \
     python3-pip screen libpam0g-dev unzip curl shunit2 \
     python3-sphinx \
-  && \
-  pip3 install javasphinx recommonmark sphinx-rtd-theme sphinxcontrib-plantuml
+
+RUN pip3 install javasphinx recommonmark sphinx-rtd-theme sphinxcontrib-plantuml sphinx-a4doc
 
 # Alternative, if javasphinx fails because python3-sphinx is too recent:
 # (`_l` not found):
diff --git a/src/test/java/org/caosdb/server/query/QueryTest.java b/src/test/java/org/caosdb/server/query/QueryTest.java
index 1827ceaf576bfba79059d557fa1422839cad8d28..986122ca46307173c6c9f80b2085db3f1776904a 100644
--- a/src/test/java/org/caosdb/server/query/QueryTest.java
+++ b/src/test/java/org/caosdb/server/query/QueryTest.java
@@ -61,20 +61,23 @@ public class QueryTest {
 
   @Test
   public void testGetKey() {
-    assertEquals("E_enameF_POV(pname,=,val1)", getCacheKey("FIND ename WITH pname = val1"));
-    assertEquals("E_enameF_POV(pname,=,val1)", getCacheKey("COUNT ename WITH pname = val1"));
+    assertEquals("R_RECORDE_enameF_POV(pname,=,val1)", getCacheKey("FIND ename WITH pname = val1"));
     assertEquals(
-        "E_enameF_POV(pname,=,val1)", getCacheKey("SELECT bla FROM ename WITH pname = val1"));
-    assertEquals("E_enameF_POV(pname,null,null)", getCacheKey("SELECT bla FROM ename WITH pname"));
+        "R_RECORDE_enameF_POV(pname,=,val1)", getCacheKey("COUNT ename WITH pname = val1"));
     assertEquals(
-        "E_enameF_maxPOV(pname,null,null)",
+        "R_RECORDE_enameF_POV(pname,=,val1)",
+        getCacheKey("SELECT bla FROM ename WITH pname = val1"));
+    assertEquals(
+        "R_RECORDE_enameF_POV(pname,null,null)", getCacheKey("SELECT bla FROM ename WITH pname"));
+    assertEquals(
+        "R_RECORDE_enameF_maxPOV(pname,null,null)",
         getCacheKey("SELECT bla FROM ename WITH THE GREATEST pname"));
 
     assertEquals(
         "R_RECORDE_enameF_POV(pname,=,val1)", getCacheKey("FIND RECORD ename WITH pname = val1"));
     assertEquals("R_ENTITYF_POV(pname,=,val1)", getCacheKey("COUNT ENTITY WITH pname = val1"));
     assertEquals(
-        "E_enameF_Conj(POV(pname,=,val1)POV(ename2,=,val2))",
+        "R_RECORDE_enameF_Conj(POV(pname,=,val1)POV(ename2,=,val2))",
         getCacheKey("SELECT bla FROM ename WITH pname = val1 AND ename2 = val2"));
 
     assertEquals("V_R_ENTITYF_ID(,>,2)", getCacheKey("FIND ANY VERSION OF ENTITY WITH ID > 2"));
@@ -83,14 +86,14 @@ public class QueryTest {
     assertEquals(
         "R_ENTITYF_SAT(asdf/asdf)", getCacheKey("FIND ENTITY WHICH IS STORED AT asdf/asdf"));
     assertEquals(
-        "E_enameF_POV(ref1,null,null)SUB(POV(pname,>,val1)",
+        "R_RECORDE_enameF_POV(ref1,null,null)SUB(POV(pname,>,val1)",
         getCacheKey("FIND ename WITH ref1 WITH pname > val1 "));
     assertEquals(
-        "E_enameF_@(ref1,null)SUB(POV(pname,>,val1)",
+        "R_RECORDE_enameF_@(ref1,null)SUB(POV(pname,>,val1)",
         getCacheKey("FIND ename WHICH IS REFERENCED BY ref1 WITH pname > val1 "));
 
     assertEquals(
-        "U_anonymous@anonymousE_enameF_POV(pname,=,val1)",
+        "U_anonymous@anonymousR_RECORDE_enameF_POV(pname,=,val1)",
         getCacheKeyWithUser("FIND ename WITH pname = val1"));
   }
 
@@ -103,7 +106,7 @@ public class QueryTest {
 
     q.optimize();
     assertNull(q.getEntity());
-    assertEquals(Query.Role.ENTITY, q.getRole());
+    assertEquals(Query.Role.RECORD, q.getRole());
   }
 
   /**