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/webcaosdb.js b/src/core/js/webcaosdb.js
index 03e372919377e537017a0f2917c0d66db9ed580e..955bdd0b777033531242b8533055cb925d96bae4 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -1378,10 +1378,66 @@ var queryForm = new function () {
         this.restoreLastQuery(form, () => window.sessionStorage.lastQuery);
         this.bindOnClick(form, (set) => {
             window.sessionStorage.lastQuery = set;
-            return null;
         });
+        const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = "PathObject, MapObject";
+        this.initFreeSearch(form, `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}`);
     };
 
+    this.role_name_facet_select = undefined;
+
+    /**
+     * 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*".
+     *
+     * @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.
+     */
+    this.initFreeSearch = function (form, options) {
+        const textArea = $(form).find(".caosdb-query-textarea");
+        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 ? "selected" : ""}>${option}</option>`);
+            }
+            $(form).find(".input-group").prepend(select);
+            this.role_name_facet_select = select[0];
+
+            const switchFreeSearch = (text_area) => {
+                if(this._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;
+        }
+        this.role_name_facet_select = undefined;
+        return false;
+    }
+
     this.restoreLastQuery = function (form, getter) {
         if (form == null) {
             throw new Error("form was null");
@@ -1403,6 +1459,15 @@ var queryForm = new function () {
         location.href = connection.getBasePath() + "Entity/?" + pagingparam + "query=" + query;
     }
 
+    this.getRoleNameFacet = function () {
+        if (this.role_name_facet_select) {
+            const result = this.role_name_facet_select.value;
+            window.localStorage["role_name_facet_option"] = result;
+            return result;
+        }
+        return "RECORD";
+    }
+
     const _splitSearchTermsPattern = /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g;
 
     /**
@@ -1421,6 +1486,11 @@ var queryForm = new function () {
         return Array.from((query + " ").matchAll(_splitSearchTermsPattern), (m) => m[1] || m[2] || m[3]).filter((word) => word);
     }
 
+    this._isCql = function (query) {
+       query = query.toUpperCase().trim();
+       return (query.startsWith("FIND") || query.startsWith("COUNT") || query.startsWith("SELECT"));
+    }
+
     this.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,19 +1504,22 @@ 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 (!queryForm._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("'", `\\'`)}*'`);
                 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 = queryForm.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);
 
@@ -1456,15 +1529,15 @@ var queryForm = new function () {
             }
 
             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-query-textarea");
+            textField.blur();
+            textField.prop("disabled", true);
+        };
 
         // handler for the form
         form.onsubmit = function (e) {
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index 328a057f19af953399e08867085f6e04b5e785c5..b47c98a6547b89192fd003c2734dfde5e89ee7f4 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -1111,6 +1111,7 @@ 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");
     }
 });
 
@@ -1196,14 +1197,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 +1213,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 +1222,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 +1252,40 @@ 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-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.role_name_facet_select, "role_name_facet_select is undefined 1");
+    assert.notOk(queryForm.initFreeSearch(), "not initialized");
+
+    assert.notOk(queryForm.role_name_facet_select, "role_name_facet_select is undefined 2");
+
+    assert.equal(form.find("select").length, 0, "no select present");
+    assert.equal(queryForm.getRoleNameFacet(), "RECORD");
+
+
+    // after initialization
+    assert.ok(queryForm.initFreeSearch(form, "Person, Experiment, Sample"), "initialized");
+    assert.ok(queryForm.role_name_facet_select, "role_name_facet_select is defined");
+    assert.equal(queryForm.role_name_facet_select.tagName, "SELECT", "role_name_facet_select is SELECT");
+    assert.equal(form.find("select").length, 1, "select is present");
+
+    assert.equal(queryForm.getRoleNameFacet(), "Person");
+    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) {}