diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1456f1850733a6f9f9d5d286634ee32d4639b504..1f1b0ed001204cb31f8f836f475c5c63bf426c10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,12 +8,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
+* [#191](https://gitlab.com/caosdb/caosdb-webui/-/issues/191) - "Configure the
+  RecordType which is searched by the simple search."
+  A list of entity roles and names can be specified via the newly added
+  `BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS` build variable. See the docstring
+  of `queryForm.initFreeSearch` for more information.
+
 ### Changed (for changes in existing functionality)
 
 * Version bump of caosdb_map module (0.5.0):
   * Added configurable entityLayers
   * Changed name of the icon option to icon_options, because that name better
     distiguished the options from the result icon object.
+* New behavior of the "Enter" key in the query input text field: When pressed
+  when the autocompletion drop-down is open, the enter key selects an option
+  from the autocompletion (this is as it was before). But when the "Enter" key
+  is pressed, when the autocompletion drop-down is not open, the query is being
+  submitted.
 
 ### Deprecated
 
diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index 078aed9b54caca678112ae443965d076c2b96915..9b8f8095befc01f3f9fba610ee61efbaa8c24fc5 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -76,6 +76,27 @@ BUILD_NAVBAR_BRAND_NAME=CaosDB
 BUILD_TITLE_BRAND_NAME=CaosDB
 BUILD_FAVICON=pics/caosdb_logo_42.png
 
+##############################################################################
+# queryForm properties
+##############################################################################
+
+# Initialize the free search to generate search queries which search only
+# within one of several options of roles or entity names.
+#
+# E.g. when `options` is "Person, Experiment, Sample" the user can select
+# one of these options before submitting the query. When the user types in
+# something that doesn't looks like a CQL-query, a query is generated
+# instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE
+# "*something*".
+#
+# Note: This feature is disabled by default. Enable it by specifying the
+# build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a
+# comma-separated list of **properly quoted** expressions, e.g.
+# "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'".
+# Otherwise, the server will throw a syntax error.
+BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS=
+
+
 ##############################################################################
 # Footer properties
 ##############################################################################
@@ -122,6 +143,7 @@ BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM_TOOLBOX="Tools"
 # MODULE_DEPENDENCIES array.
 ##############################################################################
 JS_DIST_BUNDLE=TRUE
+
 ##############################################################################
 # TRUE means that all javascript sources which are no mentioned in the
 # MODULE_DEPENDENCIES array will be added in no particular order into the
@@ -129,6 +151,7 @@ JS_DIST_BUNDLE=TRUE
 # appear in the dit file) you need to add them to the MODULE_DEPENDENCIES.
 ##############################################################################
 AUTO_DISCOVER_MODULES=TRUE
+
 ##############################################################################
 # Module dependencies
 # Override or extend to specify the order of js files in the resulting
@@ -147,6 +170,7 @@ MODULE_DEPENDENCIES=(
     webcaosdb.js
     pako.js
     utif.js
+    ext_version_history.js
     caosdb.js
     form_elements.js
     ext_autocomplete.js
diff --git a/src/core/js/ext_autocomplete.js b/src/core/js/ext_autocomplete.js
index 932a9466a83f17594894ad389f6535bcd6caffa2..02ae1199494ae3634afa16ad417b886f19068ee0 100644
--- a/src/core/js/ext_autocomplete.js
+++ b/src/core/js/ext_autocomplete.js
@@ -179,6 +179,19 @@ var ext_autocomplete = new function () {
      */
     this.switch_on_completion = function () {
         var field = $("#caosdb-query-textarea");
+
+        // submit on "enter" when the drop-down menu is not visible.
+        field.on("keydown", (e) => {
+            if(e.originalEvent.keyCode == 13) { // Enter
+                if($(e.target).siblings(".bootstrap-autocomplete.show").length == 0) {
+                    $(".caosdb-search-btn").click();
+                } else {
+                    // don't submit - the user is just selecting something
+                    e.originalEvent.preventDefault();
+                }
+            }
+        });
+
         field.attr("autocomplete", "off");
         field.toggleClass("basicAutoComplete", true);
         field.autoComplete({
diff --git a/src/core/js/ext_version_history.js b/src/core/js/ext_version_history.js
new file mode 100644
index 0000000000000000000000000000000000000000..ea65481589c2f95405833f7b4a929f450ea6da96
--- /dev/null
+++ b/src/core/js/ext_version_history.js
@@ -0,0 +1,238 @@
+/*
+ * ** header v3.0
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2019-2022 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2019-2022 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2022 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+ * Copyright (C) 2022 Daniel Hornung <d.hornung@indiscale.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ * ** end header
+ */
+
+
+/**
+ * This module provides the functionality to load the full version history (for
+ * privileged users) and export it to tsv.
+ *
+ * @module version_history
+ */
+var version_history = new function () {
+
+    const logger = log.getLogger("version_history");
+    this.logger = logger;
+
+    this._has_version_fragment = function () {
+        const fragment = window.location.hash.substr(1);
+        return fragment === 'version_history';
+    }
+
+    this._get = connection.get;
+    /**
+     * Retrieve the version history of an entity and return a table with the
+     * history.
+     *
+     * @function retrieve_history
+     * @param {string} entity - the entity id with or without version id.
+     * @return {HTMLElement} A table with the version history.
+     */
+    this.retrieve_history = async function (entity) {
+        const xml = this._get(transaction
+            .generateEntitiesUri([entity]) + "?H");
+        const html = (await transformation.transformEntities(xml))[0];
+        const history_table = $(html).find(".caosdb-f-entity-version-history");
+        return history_table[0];
+    }
+
+    /**
+     * Initalize the buttons for loading the version history.
+     *
+     * The buttons are visible when the entity has only the normal version info
+     * attached and the current user has the permissions to retrieve the
+     * version history.
+     *
+     * The buttons trigger the retrieval of the version history and append the
+     * version history to the version info modal.
+     *
+     * @function init_load_history_buttons
+     */
+    this.init_load_history_buttons = function () {
+        for (let entity of $(".caosdb-entity-panel")) {
+            const is_permitted = hasEntityPermission(entity, "RETRIEVE:HISTORY");
+            if (!is_permitted) {
+                continue;
+            }
+            const entity_id_version = getEntityIdVersion(entity);
+            const version_info = $(entity)
+                .find(".caosdb-f-entity-version-info");
+            const button = $(version_info)
+                .find(".caosdb-f-entity-version-load-history-btn");
+            button.show();
+            button
+                .click(async () => {
+                    button.prop("disabled", true);
+                    const wait = createWaitingNotification("Retrieving full history. Please wait.");
+                    const sparse = $(version_info)
+                        .find(".caosdb-f-entity-version-history");
+                    sparse.find(".modal-body *").replaceWith(wait);
+
+                    const history_table = await version_history
+                        .retrieve_history(entity_id_version);
+                    sparse.replaceWith(history_table);
+                    version_history.init_export_history_buttons(entity);
+                    version_history.init_restore_version_buttons(entity);
+                });
+        }
+    }
+
+    /**
+     * Transform the HTML table with the version history to tsv.
+     *
+     * @function get_history_tsv
+     * @param {HTMLElement} history_table - the HTML representation of the
+     *     version history.
+     * @return {string} the version history as downloadable tsv string,
+     *     suitable for the href attribute of a link or window.location.
+     */
+    this.get_history_tsv = function (history_table) {
+        const rows = [];
+        for (let row of $(history_table).find("tr")) {
+            const cells = $(row).find(".export-data").toArray().map(x => x.textContent);
+            rows.push(cells);
+        }
+        return caosdb_utils.create_tsv_table(rows);
+    }
+
+    /**
+     * Initialize the export buttons of `entity`.
+     *
+     * The buttons are only visible when the version history is visible and
+     * trigger a download of a tsv file which contains the version history.
+     *
+     * The buttons trigger the download of a tsv file with the version history.
+     *
+     * @function init_export_history_buttons
+     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
+     *     page entities are initialized.
+     */
+    this.init_export_history_buttons = function (entity) {
+        entity = entity || $(".caosdb-entity-panel");
+        for (let version_info of $(entity)
+                .find(".caosdb-f-entity-version-info")) {
+            $(version_info).find(".caosdb-f-entity-version-export-history-btn")
+                .click(() => {
+                    const html_table = $(version_info).find("table")[0];
+                    const history_tsv = this.get_history_tsv(html_table);
+                    version_history._download_tsv(history_tsv);
+                });
+        }
+    }
+
+    /**
+     * Initialize the restore old version buttons of `entity`.
+     *
+     * The buttons are only visible when the user is allowed to update the
+     * entity.
+     *
+     * The causes a retrieve of the specified version of the entity and then an
+     * update that restores that version.
+     *
+     * @function init_restore_version_buttons
+     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
+     *     page entities are initialized.
+     */
+    this.init_restore_version_buttons = function (entity) {
+        var entities = [entity] || $(".caosdb-entity-panel");
+
+        for (let _entity of entities) {
+            // initialize buttons only if the user is allowed to update the entity
+            if (hasEntityPermission(_entity, "UPDATE:*") || hasEntityPermission(_entity, "UPDATE:DESCRIPTION")) {
+                for (let version_info of
+                        $(_entity).find(".caosdb-f-entity-version-info")) {
+                    // find the restore button
+                    $(version_info).find(".caosdb-f-entity-version-restore-btn")
+                        .toggleClass("d-none", false) // show button
+                        .click(async (eve) => {
+                            // the version id is stored in the restore button's
+                            // data-version-id attribute
+                            const versionid = eve.delegateTarget.getAttribute("data-version-id")
+                            const reload = () => {
+                                window.location.reload();
+                            }
+                            const _alert = form_elements.make_alert({
+                                title: "Warning",
+                                message: "You are going to restore this version of the entity.",
+                                proceed_callback: async () => {
+                                    try {
+                                        await restore_old_version(versionid);
+                                        $(_alert).remove();
+                                        // reload after sucessful update
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-success" role="alert">Restore successful! <p>You are being forwarded to the latest version of this entity or you can click <a href="#" onclick="window.location.reload()">here</a>.</p></div>`));
+                                        setTimeout(reload, 5000);
+                                    } catch (e) {
+                                        logger.error(e);
+                                        // print errors in an alert div
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-danger alert-dismissible " role="alert"> <button class="btn-close" data-bs-dismiss="alert" aria-label="close"></button> Restore failed! <p>${e.message}</p></div>`));
+
+                                    }
+                                },
+                                cancel_callback: () => {
+                                    // do nothing
+                                    $(_alert).remove();
+                                    $(version_info).find("table").show();
+                                },
+                                proceed_text: "Yes, restore!",
+                                remember_my_decision_id: "restore_entity",
+                            });
+
+                            $(version_info).find("table").after(_alert).hide();
+                            $(_alert).addClass("text-end");
+                        });
+                }
+            }
+        }
+    }
+
+    this._download_tsv = function (tsv_link) {
+        window.location.href = tsv_link;
+    }
+
+
+    /**
+     * @function init
+     */
+    this.init = function () {
+        this.init_load_history_buttons();
+        this.init_export_history_buttons();
+        this.init_restore_version_buttons();
+
+        // check for the version_history fragment and open the modal if present.
+        if (this._has_version_fragment()) {
+            const first_entity = $(".caosdb-entity-panel")[0];
+            if (first_entity && hasEntityPermission(first_entity, "RETRIEVE:HISTORY")) {
+                logger.debug("Showing full version modal for first entity");
+                const version_button = $(first_entity).find(".caosdb-f-entity-version-button");
+                version_button.click();
+                const full_version_history_button = $(first_entity).find(".caosdb-f-entity-version-load-history-btn");
+                full_version_history_button.click();
+            }
+        }
+    }
+}
+
+caosdb_modules.register(version_history);
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index 03e372919377e537017a0f2917c0d66db9ed580e..bfe0eb35c8e72e6493ad273ae0ed5a727019c628 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -26,6 +26,13 @@
  */
 'use strict';
 
+/**
+ * Core functionality of the CaosDB web interface.
+ *
+ * @module webcaosdb
+ * @global
+ */
+
 window.addEventListener('error', (e) => globalError(e.error));
 
 var globalError = function (error) {
@@ -61,6 +68,9 @@ var globalClassNames = new function () {
 
 /**
  * navbar module contains convenience functions for the navbar.
+ *
+ * @module navbar
+ * @global
  */
 this.navbar = new function () {
 
@@ -299,6 +309,10 @@ this.navbar = new function () {
 }
 
 
+/**
+ * @module caosdb_utils
+ * @global
+ */
 this.caosdb_utils = new function () {
     this.assert_string = function (obj, name, optional = false) {
         if (typeof obj === "undefined" && optional) {
@@ -370,6 +384,9 @@ this.caosdb_utils = new function () {
 
 /**
  * connection module contains all ajax calls.
+ *
+ * @module connection
+ * @global
  */
 this.connection = new function () {
     const logger = log.getLogger("connection");
@@ -544,6 +561,9 @@ this.connection = new function () {
 /**
  * transformation module contains all code for tranforming xml into html via
  * xslt.
+ *
+ * @module transformation
+ * @global
  */
 this.transformation = new function () {
     /**
@@ -656,6 +676,9 @@ this.transformation = new function () {
 /**
  * transaction module contains all code for insertion, update and deletion of 
  * entities. Currently, only updates are implemented.
+ *
+ * @module transaction
+ * @global
  */
 this.transaction = new function () {
     this.classNameUpdateForm = "caosdb-update-entity-form";
@@ -995,205 +1018,9 @@ this.transaction = new function () {
 }
 
 /**
- * This module provides the functionality to load the full version history (for
- * privileged users) and export it to tsv.
+ * @module paging
+ * @global
  */
-var version_history = new function () {
-
-    const logger = log.getLogger("version_history");
-    this.logger = logger;
-
-    this._has_version_fragment = function () {
-        const fragment = window.location.hash.substr(1);
-        return fragment === 'version_history';
-    }
-
-    this._get = connection.get;
-    /**
-     * Retrieve the version history of an entity and return a table with the
-     * history.
-     *
-     * @param {string} entity - the entity id with or without version id.
-     * @return {HTMLElement} A table with the version history.
-     */
-    this.retrieve_history = async function (entity) {
-        const xml = this._get(transaction
-            .generateEntitiesUri([entity]) + "?H");
-        const html = (await transformation.transformEntities(xml))[0];
-        const history_table = $(html).find(".caosdb-f-entity-version-history");
-        return history_table[0];
-    }
-
-    /**
-     * Initalize the buttons for loading the version history.
-     *
-     * The buttons are visible when the entity has only the normal version info
-     * attached and the current user has the permissions to retrieve the
-     * version history.
-     *
-     * The buttons trigger the retrieval of the version history and append the
-     * version history to the version info modal.
-     */
-    this.init_load_history_buttons = function () {
-        for (let entity of $(".caosdb-entity-panel")) {
-            const is_permitted = hasEntityPermission(entity, "RETRIEVE:HISTORY");
-            if (!is_permitted) {
-                continue;
-            }
-            const entity_id_version = getEntityIdVersion(entity);
-            const version_info = $(entity)
-                .find(".caosdb-f-entity-version-info");
-            const button = $(version_info)
-                .find(".caosdb-f-entity-version-load-history-btn");
-            button.show();
-            button
-                .click(async () => {
-                    button.prop("disabled", true);
-                    const wait = createWaitingNotification("Retrieving full history. Please wait.");
-                    const sparse = $(version_info)
-                        .find(".caosdb-f-entity-version-history");
-                    sparse.find(".modal-body *").replaceWith(wait);
-
-                    const history_table = await version_history
-                        .retrieve_history(entity_id_version);
-                    sparse.replaceWith(history_table);
-                    version_history.init_export_history_buttons(entity);
-                    version_history.init_restore_version_buttons(entity);
-                });
-        }
-    }
-
-    /**
-     * Transform the HTML table with the version history to tsv.
-     *
-     * @param {HTMLElement} history_table - the HTML representation of the
-     *     version history.
-     * @return {string} the version history as downloadable tsv string,
-     *     suitable for the href attribute of a link or window.location.
-     */
-    this.get_history_tsv = function (history_table) {
-        const rows = [];
-        for (let row of $(history_table).find("tr")) {
-            const cells = $(row).find(".export-data").toArray().map(x => x.textContent);
-            rows.push(cells);
-        }
-        return caosdb_utils.create_tsv_table(rows);
-    }
-
-    /**
-     * Initialize the export buttons of `entity`.
-     *
-     * The buttons are only visible when the version history is visible and
-     * trigger a download of a tsv file which contains the version history.
-     *
-     * The buttons trigger the download of a tsv file with the version history.
-     *
-     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
-     *     page entities are initialized.
-     */
-    this.init_export_history_buttons = function (entity) {
-        entity = entity || $(".caosdb-entity-panel");
-        for (let version_info of $(entity)
-                .find(".caosdb-f-entity-version-info")) {
-            $(version_info).find(".caosdb-f-entity-version-export-history-btn")
-                .click(() => {
-                    const html_table = $(version_info).find("table")[0];
-                    const history_tsv = this.get_history_tsv(html_table);
-                    version_history._download_tsv(history_tsv);
-                });
-        }
-    }
-
-    /**
-     * Initialize the restore old version buttons of `entity`.
-     *
-     * The buttons are only visible when the user is allowed to update the
-     * entity.
-     *
-     * The causes a retrieve of the specified version of the entity and then an
-     * update that restores that version.
-     *
-     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
-     *     page entities are initialized.
-     */
-    this.init_restore_version_buttons = function (entity) {
-        var entities = [entity] || $(".caosdb-entity-panel");
-
-        for (let _entity of entities) {
-            // initialize buttons only if the user is allowed to update the entity
-            if (hasEntityPermission(_entity, "UPDATE:*") || hasEntityPermission(_entity, "UPDATE:DESCRIPTION")) {
-                for (let version_info of
-                        $(_entity).find(".caosdb-f-entity-version-info")) {
-                    // find the restore button
-                    $(version_info).find(".caosdb-f-entity-version-restore-btn")
-                        .toggleClass("d-none", false) // show button
-                        .click(async (eve) => {
-                            // the version id is stored in the restore button's
-                            // data-version-id attribute
-                            const versionid = eve.delegateTarget.getAttribute("data-version-id")
-                            const reload = () => {
-                                window.location.reload();
-                            }
-                            const _alert = form_elements.make_alert({
-                                title: "Warning",
-                                message: "You are going to restore this version of the entity.",
-                                proceed_callback: async () => {
-                                    try {
-                                        await restore_old_version(versionid);
-                                        $(_alert).remove();
-                                        // reload after sucessful update
-                                        $(version_info).find(".modal-body").prepend(
-                                            $(`<div class="alert alert-success" role="alert">Restore successful! <p>You are being forwarded to the latest version of this entity or you can click <a href="#" onclick="window.location.reload()">here</a>.</p></div>`));
-                                        setTimeout(reload, 5000);
-                                    } catch (e) {
-                                        logger.error(e);
-                                        // print errors in an alert div
-                                        $(version_info).find(".modal-body").prepend(
-                                            $(`<div class="alert alert-danger alert-dismissible " role="alert"> <button class="btn-close" data-bs-dismiss="alert" aria-label="close"></button> Restore failed! <p>${e.message}</p></div>`));
-
-                                    }
-                                },
-                                cancel_callback: () => {
-                                    // do nothing
-                                    $(_alert).remove();
-                                    $(version_info).find("table").show();
-                                },
-                                proceed_text: "Yes, restore!",
-                                remember_my_decision_id: "restore_entity",
-                            });
-
-                            $(version_info).find("table").after(_alert).hide();
-                            $(_alert).addClass("text-end");
-                        });
-                }
-            }
-        }
-    }
-
-    this._download_tsv = function (tsv_link) {
-        window.location.href = tsv_link;
-    }
-
-
-    this.init = function () {
-        this.init_load_history_buttons();
-        this.init_export_history_buttons();
-        this.init_restore_version_buttons();
-
-        // check for the version_history fragment and open the modal if present.
-        if (this._has_version_fragment()) {
-            const first_entity = $(".caosdb-entity-panel")[0];
-            if (first_entity && hasEntityPermission(first_entity, "RETRIEVE:HISTORY")) {
-                logger.debug("Showing full version modal for first entity");
-                const version_button = $(first_entity).find(".caosdb-f-entity-version-button");
-                version_button.click();
-                const full_version_history_button = $(first_entity).find(".caosdb-f-entity-version-load-history-btn");
-                full_version_history_button.click();
-            }
-        }
-    }
-}
-
 var paging = new function () {
 
     this.defaultPageLen = 10;
@@ -1373,16 +1200,96 @@ var paging = new function () {
     }
 };
 
-var queryForm = new function () {
-    this.init = function (form) {
-        this.restoreLastQuery(form, () => window.sessionStorage.lastQuery);
-        this.bindOnClick(form, (set) => {
+/**
+ * Extend the functionality of the pure html query panel.
+ *
+ * @module queryForm
+ * @global
+ */
+var queryForm = function () {
+
+    const init = function (form) {
+        queryForm.restoreLastQuery(form, () => window.sessionStorage.lastQuery);
+        queryForm.bindOnClick(form, (set) => {
             window.sessionStorage.lastQuery = set;
-            return null;
         });
+        const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = "";
+        queryForm.initFreeSearch(form, `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}`);
     };
 
-    this.restoreLastQuery = function (form, getter) {
+    const logger = log.getLogger("queryForm");
+    var role_name_facet_select = undefined;
+
+    const _isCql = function (query) {
+       query = query.toUpperCase().trim();
+       return (query.startsWith("FIND") || query.startsWith("COUNT") || query.startsWith("SELECT"));
+    }
+
+    /**
+     * Initialize the free search to generate search queries which search only
+     * within one of several options of roles or entity names.
+     *
+     * E.g. when `options` is "Person, Experiment, Sample" the user can select
+     * one of these options before submitting the query. When the user types in
+     * something that doesn't looks like a CQL-query, a query is generated
+     * instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE
+     * "*something*".
+     *
+     * Note: This feature is disabled by default. Enable it by specifying the
+     * build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a
+     * comma-separated list of **properly quoted** expressions, e.g.
+     * "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'".
+     * Otherwise, the server will throw a syntax error.
+     *
+     * @function initFreeSearch
+     * @param {HTMLElement} form - the form which will be initialized for the
+     *     free search.
+     * @param {string} options - comma-separated list of options.
+     * @return {boolean} - true if the initialization was successful, false
+     *     otherwise.
+     */
+    const initFreeSearch = function (form, options) {
+        const textArea = $(form).find(".caosdb-f-query-textarea");
+        logger.trace("initFreeSearch", form, textArea, options);
+        if (textArea.length > 0 && options && options != "") {
+            const lastQuery = window.localStorage["freeTextQuery:" + textArea[0].value];
+            if (lastQuery) {
+                textArea[0].value = lastQuery;
+            }
+
+            const selected = window.localStorage["role_name_facet_option"];
+            const select = $(`<select class="btn btn-secondary"/>`);
+            for (let option of options.split(",")) {
+              select.append(`<option ${selected === option.trim() ? "selected" : ""}>${option}</option>`);
+            }
+            $(form).find(".input-group").prepend(select);
+            role_name_facet_select = select[0];
+
+            const switchFreeSearch = (text_area) => {
+                if(_isCql(text_area.value)) {
+                    select.hide();
+                    $(text_area).css({"border-top-left-radius": "0.375rem", "border-bottom-left-radius": "0.375rem"});
+                } else {
+                    select.show();
+                    $(text_area).css({});
+                }
+            }
+
+            switchFreeSearch(textArea[0]);
+
+            textArea.on("keydown", (e) => {
+                switchFreeSearch(e.target);
+            });
+            return true;
+        }
+        role_name_facet_select = undefined;
+        return false;
+    }
+
+    /**
+     * @function restoreLastQuery
+     */
+    const restoreLastQuery = function (form, getter) {
         if (form == null) {
             throw new Error("form was null");
         }
@@ -1392,10 +1299,11 @@ var queryForm = new function () {
     };
 
     /**
-     * @value {string} query - the query string.
+     * @function redirect
+     * @param {string} query - the query string.
      * @param {string} paging - the paging string, e.g. 0L10.
      */
-    this.redirect = function (query, paging) {
+    const redirect = function (query, paging) {
         var pagingparam = ""
         if (paging && paging.length > 0) {
             pagingparam = "P=" + paging + "&";
@@ -1403,6 +1311,20 @@ var queryForm = new function () {
         location.href = connection.getBasePath() + "Entity/?" + pagingparam + "query=" + query;
     }
 
+    /**
+     * Read out the selector for the role/name facet of the query. Return
+     * "RECORD" if the selector is disabled.
+     * @function getRoleNameFacet
+     */
+    const getRoleNameFacet = function () {
+        if (role_name_facet_select) {
+            const result = role_name_facet_select.value;
+            window.localStorage["role_name_facet_option"] = result;
+            return result;
+        }
+        return "RECORD";
+    }
+
     const _splitSearchTermsPattern = /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g;
 
     /**
@@ -1413,15 +1335,30 @@ var queryForm = new function () {
      * enclosing quotation marks are being stripped. Currently no support for
      * escape sequences for quotation marks.
      *
+     * @function splitSearchTerms
      * @param {string} query - complete query string.
      * @return {string[]} array of the search terms.
      */
-    this.splitSearchTerms = function (query) {
+    const splitSearchTerms = function (query) {
         // add empty space at the end, so every matching group ends with it -> easier regex. Also, undefined is filtered out
         return Array.from((query + " ").matchAll(_splitSearchTermsPattern), (m) => m[1] || m[2] || m[3]).filter((word) => word);
     }
 
-    this.bindOnClick = function (form, setter) {
+    /**
+     * Is the query a SELECT field,... FROM entity query?
+     *
+     * @function isSelectQuery
+     * @param {HTMLElement} query, the query to be tested.
+     * @return {Boolean}
+     */
+    const isSelectQuery = function (query) {
+        return query.toUpperCase().startsWith("SELECT");
+    }
+
+    /**
+     * @function bindOnClick
+     */
+    const bindOnClick = function (form, setter) {
         if (setter == null || typeof (setter) !== 'function' || setter.length !== 1) {
             throw new Error("setter must be a function with one param");
         }
@@ -1434,37 +1371,40 @@ var queryForm = new function () {
         var submithandler = function () {
             // store current query
             var queryField = form.query;
-            var value = queryField.value.toUpperCase().trim();
+            var value = queryField.value;
             if (typeof value == "undefined" || value.length == 0) {
                 return;
             }
-            if (!(value.startsWith("FIND") || value.startsWith("COUNT") || value.startsWith("SELECT"))) {
+            if (!_isCql(value)) {
                 // split words in query field at space and create query fragments
-                var words = queryForm.splitSearchTerms(queryField.value).map(word => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'`);
+                var words = splitSearchTerms(queryField.value).map(word => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'`);
                 if (!words.length) {
                     return false;
                 }
-                var query_string = "FIND ENTITY WHICH HAS ";
-                // send a query that combines all fragments with an AND 
-                queryField.value = query_string + words.join(" AND ");
+                const e = getRoleNameFacet();
+                const query_string = `FIND ${e} WHICH HAS ` + words.join(" AND ");
+                queryField.value = query_string;
+
+                // store original value of the text field
+                window.localStorage["freeTextQuery:" + query_string] = value;
             }
             setter(queryField.value);
 
             var paging = "";
-            if (form.P && !queryForm.isSelectQuery(queryField.value)) {
+            if (form.P && !isSelectQuery(queryField.value)) {
                 paging = form.P.value
             }
 
             queryForm.redirect(queryField.value.trim(), paging);
-        };
 
-        $("#caosdb-query-textarea").on("keydown", (e) => {
-            // prevent submit on enter
-            if (e.originalEvent.which == 13) {
-                e.originalEvent.preventDefault();
-            }
-        })
+            var btn =  $(form).find(".caosdb-search-btn");
+            btn.html(`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`);
+            btn.prop("disabled", true);
 
+            var textField =  $(form).find(".caosdb-f-query-textarea");
+            textField.blur();
+            textField.prop("disabled", true);
+        };
 
         // handler for the form
         form.onsubmit = function (e) {
@@ -1480,31 +1420,28 @@ var queryForm = new function () {
     };
 
     /**
-     * Is the query a SELECT field,... FROM entity query?
-     *
-     * @param {HTMLElement} query, the query to be tested.
-     * @return {Boolean}
+     * @function getRoleNameFacetSelect
      */
-    this.isSelectQuery = function (query) {
-        return query.toUpperCase().startsWith("SELECT");
-    }
 
-    /**
-     * Remove the (hidden) paging input from the query form.
-     * The form is changed in-place without copying it.
-     *
-     * @param {HTMLElement} form, the query form.
-     * @return {HTMLElement} the form without the paging input.
-     */
-    this.removePagingField = function (form) {
-        $(form.P).remove();
-        return form;
+    return {
+        init: init,
+        initFreeSearch: initFreeSearch,
+        isSelectQuery: isSelectQuery,
+        restoreLastQuery: restoreLastQuery,
+        redirect: redirect,
+        bindOnClick: bindOnClick,
+        splitSearchTerms: splitSearchTerms,
+        getRoleNameFacet: getRoleNameFacet,
+        getRoleNameFacetSelect: () => role_name_facet_select,
     }
-};
+}();
 
 
-/**
+/*
  * Small module containing only a converter from markdown to html.
+ *
+ * @module markdown
+ * @global
  */
 this.markdown = new function () {
     this.dependencies = ["showdown", "caosdb_utils"];
@@ -1538,6 +1475,10 @@ this.markdown = new function () {
     });
 }
 
+/**
+ * @module hintMessages
+ * @global
+ */
 var hintMessages = new function () {
     this.init = function () {
         for (var entity of $('.caosdb-entity-panel')) {
@@ -1992,7 +1933,6 @@ function initOnDocumentReady() {
     }
     caosdb_modules.init();
     navbar.init();
-    version_history.init();
 
     if ("${BUILD_MODULE_USER_MANAGEMENT}" == "ENABLED") {
         caosdb_modules.register(user_management);
@@ -2015,6 +1955,7 @@ function initOnDocumentReady() {
  *
  * Singleton which is globally available under caosdb_modules.
  *
+ * @class _CaosDBModules
  * @property {boolean} auto_init - if modules are initialized automatically
  *   after beeing registered, or when `this.init_all()` is being called.
  */
diff --git a/src/core/xsl/query.xsl b/src/core/xsl/query.xsl
index 702a390f28ada96e140f40f10218b1740ab10700..6497cc9c64f7921c2b26b4ccd6535d181ddfeb4b 100644
--- a/src/core/xsl/query.xsl
+++ b/src/core/xsl/query.xsl
@@ -359,7 +359,7 @@
         </xsl:attribute>
         <input id="caosdb-query-paging-input" name="P" type="hidden" value="0L10"/>
         <div class="input-group">
-          <input class="form-control" id="caosdb-query-textarea" name="query" placeholder="E.g. 'FIND Experiment'" rows="1" style="resize: vertical;" type="text"></input>
+          <input class="form-control caosdb-f-query-textarea" id="caosdb-query-textarea" name="query" placeholder="E.g. 'FIND Experiment'" rows="1" style="resize: vertical;" type="text"></input>
             <a class="btn btn-secondary caosdb-search-btn" href="#" title="Click to execute the query.">
               <i class="bi-search"></i>
             </a>
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index 328a057f19af953399e08867085f6e04b5e785c5..534dc86c485ccfac184bb05bac17d595addaa3d6 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -1111,19 +1111,10 @@ QUnit.test("initEntity", function (assert) {
 QUnit.module("webcaosdb.js - queryForm", {
     before: function (assert) {
         assert.ok(queryForm, "queryForm is defined");
+        assert.notOk(queryForm.initFreeSearch(), "free search reset");
     }
 });
 
-QUnit.test("removePagingField", function (assert) {
-    assert.ok(queryForm.removePagingField, "function available.");
-    assert.throws(() => queryForm.removePagingField(), "null param throws.");
-    let form = $('<form><input name="P"></form>')[0];
-    assert.ok(form.P, "before: paging available.");
-    queryForm.removePagingField(form);
-    assert.notOk(form.P, "after: paging removed.");
-
-});
-
 QUnit.test("isSelectQuery", function (assert) {
     assert.ok(queryForm.isSelectQuery, "function available.");
     assert.throws(() => queryForm.isSelectQuery(), "null param throws.");
@@ -1196,14 +1187,14 @@ QUnit.test("bindOnClick", function (assert) {
     assert.equal(storage(), undefined, "before2: storage empty.");
     form.getElementsByClassName("caosdb-search-btn")[0].onclick();
 
-    assert.equal(storage(), "FIND ENTITY WHICH HAS A PROPERTY LIKE '*freetext*'", "after2: storage not empty.");
+    assert.equal(storage(), "FIND RECORD WHICH HAS A PROPERTY LIKE '*freetext*'", "after2: storage not empty.");
 
     // test the form submit handler analogously
     form.query.value = "freetext2";
     $("body").append(form);
     $(form).append(submitButton);
     submitButton.click();
-    assert.equal(storage(), "FIND ENTITY WHICH HAS A PROPERTY LIKE '*freetext2*'", "after3: storage not empty.");
+    assert.equal(storage(), "FIND RECORD WHICH HAS A PROPERTY LIKE '*freetext2*'", "after3: storage not empty.");
 
     $(form).remove();
 
@@ -1212,7 +1203,7 @@ QUnit.test("bindOnClick", function (assert) {
     $("body").append(form);
     $(form).append(submitButton);
     submitButton.click();
-    assert.equal(storage(), "FIND ENTITY WHICH HAS A PROPERTY LIKE '*free*' AND A PROPERTY LIKE '*text*' AND A PROPERTY LIKE '*3*'", "after4: storage not empty.");
+    assert.equal(storage(), "FIND RECORD WHICH HAS A PROPERTY LIKE '*free*' AND A PROPERTY LIKE '*text*' AND A PROPERTY LIKE '*3*'", "after4: storage not empty.");
 
     $(form).remove();
 
@@ -1221,7 +1212,7 @@ QUnit.test("bindOnClick", function (assert) {
     $("body").append(form);
     $(form).append(submitButton);
     submitButton.click();
-  assert.equal(storage(), `FIND ENTITY WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*"\\'bla*'`, "after5: stuff with quotation marks");
+  assert.equal(storage(), `FIND RECORD WHICH HAS A PROPERTY LIKE '*with double*' AND A PROPERTY LIKE '*and single*' AND A PROPERTY LIKE '*what\\'s wrong?*' AND A PROPERTY LIKE '*\\'*' AND A PROPERTY LIKE '*nothin\\'*' AND A PROPERTY LIKE '*"\\'bla*'`, "after5: stuff with quotation marks");
 
     // ... then with empty quotation marks. this will not trigger the query execution at all
     storage("not triggered");
@@ -1251,6 +1242,42 @@ QUnit.test("splitSearchTerms", function (assert) {
     }
 });
 
+QUnit.test("initFreeSearch", function (assert) {
+    const form = $('<form></form>');
+    const inputGroup = $('<div class="input-group"></div>');
+    const textArea = $( '<textarea class="caosdb-f-query-textarea" name="query"></textarea>');
+    const submitButton = $('<input class="caosdb-search-btn" type="submit">');
+    inputGroup.append(textArea);
+    form.append(inputGroup).append(submitButton);
+    $("body").append(form);
+
+    // without initialization
+    assert.ok(queryForm.initFreeSearch, "available");
+    assert.notOk(queryForm.getRoleNameFacetSelect(), "role_name_facet_select is undefined 1");
+    assert.notOk(queryForm.initFreeSearch(), "not initialized");
+
+    assert.notOk(queryForm.getRoleNameFacetSelect(), "role_name_facet_select is undefined 2");
+
+    assert.equal(form.find("select").length, 0, "no select present");
+    assert.equal(queryForm.getRoleNameFacet(), "RECORD");
+
+
+    window.localStorage["role_name_facet_option"] = "Sample";
+    assert.ok(queryForm.initFreeSearch(form, "Person, Experiment, Sample"), "initialized");
+
+    // after initialization
+    assert.ok(queryForm.getRoleNameFacetSelect(), "role_name_facet_select is defined");
+    assert.equal(queryForm.getRoleNameFacetSelect().tagName, "SELECT", "role_name_facet_select is SELECT");
+    assert.equal(form.find("select").length, 1, "select is present");
+
+    assert.equal(queryForm.getRoleNameFacet(), "Sample", "previously selected option is selected");
+    form.find("select")[0].value = "Experiment";
+    assert.equal(queryForm.getRoleNameFacet(), "Experiment");
+
+    // clean up
+    form.remove();
+})
+
 /* MODULE paging */
 QUnit.module("webcaosdb.js - paging", {
     before: function (assert) {}