diff --git a/CHANGELOG.md b/CHANGELOG.md
index d3689e84e60ae5e3b37ac21425e486bff9a0e6cf..420d4b5112295eec4b3485203a6f2e4be246624a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 * Module `ext_qrcode` which generates a QR Code for an entity (pointing to the
   the head or the exact version).
+* Optional functionality to bookmark all query results. Note that too many
+  bookmarks will result in the URI being too lang and bookmarks will have to be
+  cleared manually.
 
 ### Changed (for changes in existing functionality)
 
diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index 412773eb86d46151d1126d29c5e539439d68a6fe..65a2b9bc42f9e0f22dc0cd6ca6baac094e94eee7 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -49,6 +49,7 @@ BUILD_MODULE_EXT_BOTTOM_LINE=ENABLED
 BUILD_MODULE_EXT_BOTTOM_LINE_TABLE_PREVIEW=DISABLED
 BUILD_MODULE_EXT_BOTTOM_LINE_TIFF_PREVIEW=DISABLED
 BUILD_MODULE_EXT_BOOKMARKS=ENABLED
+BUILD_MODULE_EXT_ADD_QUERY_TO_BOOKMARKS=DISABLED
 BUILD_MODULE_EXT_ANNOTATION=ENABLED
 BUILD_MODULE_EXT_COSMETICS_LINKIFY=DISABLED
 BUILD_MODULE_EXT_QRCODE=ENABLED
diff --git a/src/core/js/ext_bookmarks.js b/src/core/js/ext_bookmarks.js
index d99ff362d8a26ee4818a76a624c80f55f757c419..d9da463699ff7021b9f94a877aa3d9379dffb5dc 100644
--- a/src/core/js/ext_bookmarks.js
+++ b/src/core/js/ext_bookmarks.js
@@ -2,18 +2,19 @@
  * ** header v3.0
  * This file is a part of the CaosDB Project.
  *
- * Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2020,2021 IndiScale GmbH <info@indiscale.com>
  * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@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 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.
+ * 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/>.
@@ -231,7 +232,7 @@ var ext_bookmarks = function ($, logger, config) {
      */
     const get_export_table = async function (bookmarks, preamble, tab, newline, leading_comments) {
         // TODO merge with related code in the module "caosdb_table_export".
-        preamble = ((typeof preamble == 'undefined') ? "data:text/csv;charset=utf-8,": preamble);
+        preamble = ((typeof preamble == 'undefined') ? "data:text/csv;charset=utf-8," : preamble);
         tab = tab || "%09";
         newline = newline || "%0A";
         leading_comments = (leading_comments ? leading_comments.join(newline) + newline : "");
@@ -489,10 +490,8 @@ var ext_bookmarks = function ($, logger, config) {
         counter = 0;
         update_collection([]);
 
-        // reset all buttons
-        get_bookmark_buttons().forEach((x) => {
-            set_button_state(x, false);
-        });
+        // re-init to reset all buttons
+        init();
 
         const storage_key_prefix = get_collection_prefix()
         remove_from_storage_by_prefix(storage_key_prefix);
@@ -566,6 +565,78 @@ var ext_bookmarks = function ($, logger, config) {
         collection_id = id;
     }
 
+    /**
+     * Add a button to add all query results to bookmarks.
+     */
+    const add_add_query_results_button = function () {
+        const row_id = "caosdb-f-add-query-to-bookmarks-row"
+        // do nothing if already existing
+        if ($("#" + row_id).length > 0) {
+            return;
+        }
+
+        // do nothing if no results
+        if ($(".caosdb-query-response-results").text().trim() == "0") {
+            return;
+        }
+
+        const button_html = $(`<div class="text-end" id=${row_id}>
+        <button class="btn btn-link" onclick="ext_bookmarks.add_query_results_to_bookmarks();">Bookmark all query results</button>
+</div>`)[0];
+
+        // Add to query results box
+        $(".caosdb-query-response-heading").append(button_html);
+    }
+
+    /**
+     * Execute select query and add all new ids to bookmarks.
+     */
+    const add_query_results_to_bookmarks = async function () {
+
+        const query_string = get_query_from_response();
+        const waiting_notification = createWaitingNotification(
+            "Adding results to bookmarks. Please wait and do not reload the page.");
+        const bookmarks_row = $("#caosdb-f-add-query-to-bookmarks-row");
+        bookmarks_row.find("button").hide();
+        bookmarks_row.append(waiting_notification);
+        const resp = await query(query_string);
+        for (const eid of resp) {
+            bookmark_storage.setItem(get_key(getEntityID(eid)), getEntityID(eid));
+        }
+        // re-init for correct display of counter and entities on page
+        init();
+        removeAllWaitingNotifications(bookmarks_row);
+        bookmarks_row.find("button").prop("disabled", true).show();
+    }
+
+    /**
+     * Transform a given query it to a "SELECT ID FROM ..." query.
+     *
+     * @param {string} query_string
+     */
+    const get_select_id_query_string = function (query_string) {
+        const test_string = query_string.toLowerCase();
+        const select_string = "SELECT ID FROM ";
+
+        // Will only be called on valid query results, so don't have to check
+        // for invalid query strings.
+        if (test_string.startsWith("find") || test_string.startsWith("count")) {
+            return select_string + query_string.slice(query_string.indexOf(" ") + 1);
+        }
+        if (test_string.startsWith("select")) {
+            return select_string + query_string.slice(test_string.indexOf("from ") + 5);
+        }
+    }
+
+    /**
+     * Return the SELECT query created from the contents of the query response field
+     */
+    const get_query_from_response = function () {
+
+        const orig_query = $(".caosdb-f-query-response-string")[0].innerText;
+        return get_select_id_query_string(orig_query.trim());
+    }
+
     /**
      * Initialize this module.
      */
@@ -591,6 +662,10 @@ var ext_bookmarks = function ($, logger, config) {
                 init_bookmark_buttons(e.target);
             }, true);
         }
+
+        if ("${BUILD_MODULE_EXT_ADD_QUERY_TO_BOOKMARKS}" == "ENABLED") {
+            add_add_query_results_button();
+        }
     }
 
     /**
@@ -625,7 +700,6 @@ var ext_bookmarks = function ($, logger, config) {
         if (data_getters[data_key]) {
             uncached = (await data_getters[data_key](id))
         }
-
         // don't cache if getting the information is trivial or there are other
         // reasons why this is in the data_no_cache array.
         if (data_no_cache.indexOf(data_key) == -1) {
@@ -652,6 +726,9 @@ var ext_bookmarks = function ($, logger, config) {
         get_bookmark_buttons: get_bookmark_buttons,
         init_button: init_button,
         get_bookmark_data: get_bookmark_data,
+        get_select_id_query_string: get_select_id_query_string,
+        get_query_from_response: get_query_from_response,
+        add_query_results_to_bookmarks: add_query_results_to_bookmarks,
     }
 };
 
@@ -666,10 +743,10 @@ $(document).ready(function () {
         // from the server.
         const get_path = async function (id) {
             if (id.indexOf("@") > -1) {
-              const entity = $(`[data-bmval='${id}']`);
-              if (entity.length > 0) {
-                  return getEntityPath(entity[0]) || "";
-              }
+                const entity = $(`[data-bmval='${id}']`);
+                if (entity.length > 0) {
+                    return getEntityPath(entity[0]) || "";
+                }
             }
             return $(await transaction.retrieveEntityById(id)).attr("path");
         }
@@ -681,20 +758,20 @@ $(document).ready(function () {
 
         const get_name = async function (id) {
             if (id.indexOf("@") > -1) {
-              const entity = $(`[data-bmval='${id}']`);
-              if (entity.length > 0) {
-                  return getEntityName(entity[0]) || "";
-              }
+                const entity = $(`[data-bmval='${id}']`);
+                if (entity.length > 0) {
+                    return getEntityName(entity[0]) || "";
+                }
             }
             return $(await transaction.retrieveEntityById(id)).attr("name");
         }
 
         const get_rt = async function (id) {
             if (id.indexOf("@") > -1) {
-              const entity = $(`[data-bmval='${id}']`);
-              if (entity.length > 0) {
-                  return getParents(entity[0]).join("/");
-              }
+                const entity = $(`[data-bmval='${id}']`);
+                if (entity.length > 0) {
+                    return getParents(entity[0]).join("/");
+                }
             }
             const parent_names = $(await transaction.retrieveEntityById(id))
                 .find("Parent").toArray().map(x => x.getAttribute("name"))
@@ -727,4 +804,4 @@ $(document).ready(function () {
         ext_bookmarks = ext_bookmarks($, log.getLogger("ext_bookmarks"), config);
         caosdb_modules.register(ext_bookmarks);
     }
-});
+});
\ No newline at end of file
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index dbb4e269a247cbbc40f1ea58623ec7b515dc2d57..efa28c9b39921df3e02a25ec95d4601f752fa288 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -164,19 +164,19 @@ this.navbar = new function () {
 
         // show form and hide the show_button
         const _in = () => {
-          // xs means viewport <= 768px
-          form.removeClass("d-none");
-          form.addClass("d-xs-inline-block");
-          show_button.removeClass("d-inline-block");
-          show_button.addClass("d-none");
+            // xs means viewport <= 768px
+            form.removeClass("d-none");
+            form.addClass("d-xs-inline-block");
+            show_button.removeClass("d-inline-block");
+            show_button.addClass("d-none");
         }
         // hide form and show the show_button
         const _out = () => {
-          // xs means viewport <= 768px
-          form.removeClass("d-xs-inline-block");
-          form.addClass("d-none");
-          show_button.removeClass("d-none");
-          show_button.addClass("d-inline-block");
+            // xs means viewport <= 768px
+            form.removeClass("d-xs-inline-block");
+            form.addClass("d-none");
+            show_button.removeClass("d-none");
+            show_button.addClass("d-inline-block");
         }
         show_button.on("click", () => {
             // show form...
@@ -607,7 +607,7 @@ this.transformation = new function () {
      * @return {XMLDocument} xslt script
      */
     this.retrieveEntityXsl = async function _rEX(root_template) {
-        const _root = root_template || '<xsl:template match="/" xmlns="http://www.w3.org/1999/xhtml"><div class="root"><xsl:apply-templates select="Response/*" mode="entities"/></div></xsl:template>';
+        const _root = root_template || '<xsl:template match="/" xmlns="http://www.w3.org/1999/xhtml"><div class="root"><xsl:apply-templates select="Response/child::*" mode="entities"/></div></xsl:template>';
         var entityXsl = await transformation.retrieveXsltScript("entity.xsl");
         var commonXsl = await transformation.retrieveXsltScript("common.xsl");
         var errorXsl = await transformation.retrieveXsltScript('messages.xsl');
@@ -1313,7 +1313,7 @@ var queryForm = new function () {
         var submithandler = function () {
             // store current query
             var queryField = form.query;
-            var value = queryField.value.toUpperCase();
+            var value = queryField.value.toUpperCase().trim();
             if (typeof value == "undefined" || value.length == 0) {
                 return;
             }
@@ -1327,7 +1327,7 @@ var queryForm = new function () {
                 paging = form.P.value
             }
 
-            queryForm.redirect(queryField.value, paging);
+            queryForm.redirect(queryField.value.trim(), paging);
         };
 
         $("#caosdb-query-textarea").on("keydown", (e) => {
@@ -1507,10 +1507,12 @@ function createErrorNotification(msg) {
  * Create a waiting notification with a informative message for the waiting user. 
  *
  * @param {String} info, a message for the user
+ * @param {String} id, optional, the id of the message div.  Default is empty
  * @return {HTMLElement} A div with class `caosdb-preview-waiting-notification`.
  */
-function createWaitingNotification(info) {
-    return $('<div class="' + globalClassNames.WaitingNotification + '">' + info + '</div>')[0];
+function createWaitingNotification(info, id) {
+    id = id ? `id="${id}"` : "";
+    return $(`<div class="${globalClassNames.WaitingNotification}" ${id}>${info}</div>`)[0];
 }
 
 /**
@@ -1520,7 +1522,7 @@ function createWaitingNotification(info) {
  * @return {HTMLElement} The parameter `elem`.
  */
 function removeAllWaitingNotifications(elem) {
-    $(elem.getElementsByClassName(globalClassNames.WaitingNotification)).remove();
+    $(elem).find(`.${globalClassNames.WaitingNotification}`).remove();
     return elem;
 }
 
@@ -1925,4 +1927,4 @@ class _CaosDBModules {
 
 var caosdb_modules = new _CaosDBModules()
 
-$(document).ready(initOnDocumentReady);
+$(document).ready(initOnDocumentReady);
\ No newline at end of file
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index d562bf99f7611b976e10d5349d0c99972d6d8fdd..92f08ea70645a8282f97f364c9fc4143f37afd6a 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -25,6 +25,10 @@
   <xsl:output method="html"/>
   <!-- These little colored Rs, RTs, Ps, and Fs which hilite the beginning 
         of a new entity. -->
+  <xsl:template match="Entity" mode="entity-heading-label">
+    <span class="badge caosdb-f-entity-role caosdb-label-entity me-1"
+    title="This is an entity. The role is not specified.">E</span>
+  </xsl:template>
   <xsl:template match="Property" mode="entity-heading-label">
     <span class="badge caosdb-f-entity-role caosdb-label-property me-1"
     data-entity-role="Property" title="This entity is a Property.">P</span>
@@ -98,7 +102,7 @@
     </div>
   </xsl:template>
   <!-- Main entry for ENTITIES -->
-  <xsl:template match="Property|Record|RecordType|File" mode="entities">
+  <xsl:template match="Property|Record|RecordType|File|Response/Entity" mode="entities">
     <div class="card caosdb-entity-panel mb-2">
       <xsl:apply-templates select="Version" mode="entity-version-marker"/>
       <xsl:attribute name="id">
diff --git a/src/core/xsl/query.xsl b/src/core/xsl/query.xsl
index 6a95e656402bc8fcf1ac5789f2b213f09fdc8621..702a390f28ada96e140f40f10218b1740ab10700 100644
--- a/src/core/xsl/query.xsl
+++ b/src/core/xsl/query.xsl
@@ -56,7 +56,9 @@
           <div class="col-sm-10 caosdb-overflow-box">
             <div class="caosdb-overflow-content">
               <span>Query: </span>
-              <xsl:value-of select="@string"/>
+              <span class="caosdb-f-query-response-string">
+                <xsl:value-of select="@string"/>
+              </span>
             </div>
           </div>
           <div class="col-sm-2 text-end">
diff --git a/test/core/js/modules/ext_bookmarks.js.js b/test/core/js/modules/ext_bookmarks.js.js
index 831df74231e479d5b4550524f6bc0da617c98fb3..8130fe422a5bd124ef1c6767708b036e2e904f53 100644
--- a/test/core/js/modules/ext_bookmarks.js.js
+++ b/test/core/js/modules/ext_bookmarks.js.js
@@ -42,16 +42,18 @@ QUnit.module("ext_bookmarks.js", {
     }
 });
 
-QUnit.test("parse_uri", function(assert) {
+QUnit.test("parse_uri", function (assert) {
     assert.equal(typeof ext_bookmarks.parse_uri(""), "undefined");
     assert.equal(typeof ext_bookmarks.parse_uri("asdf"), "undefined");
     assert.equal(typeof ext_bookmarks.parse_uri("https://localhost:1234/Entity/sada?sadfasd#sdfgdsf"), "undefined");
 
-    assert.propEqual(ext_bookmarks.parse_uri("safgsa/123&456&789?sadfasdf#_bm_1"),
-        {bookmarks: ["123", "456", "789"], collection_id: "1"});
+    assert.propEqual(ext_bookmarks.parse_uri("safgsa/123&456&789?sadfasdf#_bm_1"), {
+        bookmarks: ["123", "456", "789"],
+        collection_id: "1"
+    });
 });
 
-QUnit.test("get_bookmarks, clear_bookmark_storage", function(assert) {
+QUnit.test("get_bookmarks, clear_bookmark_storage", function (assert) {
     assert.propEqual(ext_bookmarks.get_bookmarks(), []);
 
     ext_bookmarks.bookmark_storage[ext_bookmarks.get_key("sdfg")] = "3456"
@@ -67,9 +69,9 @@ QUnit.test("get_export_table", async function (assert) {
     const NEWL = "%0A";
     const context_root = connection.getBasePath() + "Entity/";
     var table = await ext_bookmarks.get_export_table(
-      ["123@ver1", "456@ver2", "789@ver3", "101112", "@131415"]);
+        ["123@ver1", "456@ver2", "789@ver3", "101112", "@131415"]);
     assert.equal(table,
-      `data:text/csv;charset=utf-8,ID${TAB}Version${TAB}URI${TAB}Path${TAB}Name${TAB}RecordType${NEWL}123${TAB}ver1${TAB}${context_root}123@ver1${TAB}testpath_123@ver1${TAB}${TAB}${NEWL}456${TAB}ver2${TAB}${context_root}456@ver2${TAB}testpath_456@ver2${TAB}${TAB}${NEWL}789${TAB}ver3${TAB}${context_root}789@ver3${TAB}testpath_789@ver3${TAB}${TAB}${NEWL}101112${TAB}abcHead${TAB}${context_root}101112@abcHead${TAB}testpath_101112${TAB}${TAB}${NEWL}${TAB}131415${TAB}${context_root}@131415${TAB}testpath_@131415${TAB}${TAB}`);
+        `data:text/csv;charset=utf-8,ID${TAB}Version${TAB}URI${TAB}Path${TAB}Name${TAB}RecordType${NEWL}123${TAB}ver1${TAB}${context_root}123@ver1${TAB}testpath_123@ver1${TAB}${TAB}${NEWL}456${TAB}ver2${TAB}${context_root}456@ver2${TAB}testpath_456@ver2${TAB}${TAB}${NEWL}789${TAB}ver3${TAB}${context_root}789@ver3${TAB}testpath_789@ver3${TAB}${TAB}${NEWL}101112${TAB}abcHead${TAB}${context_root}101112@abcHead${TAB}testpath_101112${TAB}${TAB}${NEWL}${TAB}131415${TAB}${context_root}@131415${TAB}testpath_@131415${TAB}${TAB}`);
 
 });
 
@@ -115,7 +117,7 @@ QUnit.test("update_export_link", function (assert) {
 
 QUnit.test("update_collection_link", function (assert) {
     const collection_link = $(
-      `<div id="caosdb-f-bookmarks-collection-link"><a/></div>`);
+        `<div id="caosdb-f-bookmarks-collection-link"><a/></div>`);
     const a = collection_link.find("a")[0];
     $("body").append(collection_link);
 
@@ -148,7 +150,8 @@ QUnit.test("bookmark buttons", function (assert) {
     const non_button = $(`<div data-bla="sadf"/>)`);
     const outside_button = $(`<div data-bmval="id3"/>`);
     const inside_buttons = $("<div/>").append([inactive_button, active_button,
-      broken_button, non_button]);
+        broken_button, non_button
+    ]);
 
     // get_bookmark_buttons
     assert.equal(ext_bookmarks.get_bookmark_buttons("body").length, 0);
@@ -174,8 +177,51 @@ QUnit.test("bookmark buttons", function (assert) {
     assert.ok(inactive_button.is(".active"));
 
     ext_bookmarks.clear_bookmark_storage();
-    assert.notOk(inactive_button.is(".active"), "clear_bookmark_storage removes active class");
 
     inside_buttons.remove();
     outside_button.remove();
 });
+
+QUnit.test("select-query transformation", function (assert) {
+    assert.equal(
+        ext_bookmarks.get_select_id_query_string("FIND analysis"),
+        "SELECT ID FROM analysis");
+    assert.equal(
+        ext_bookmarks.get_select_id_query_string(
+            "FIND RECORD analysis WHICH HAS A date > 2012"),
+        "SELECT ID FROM RECORD analysis WHICH HAS A date > 2012");
+    assert.equal(
+        ext_bookmarks.get_select_id_query_string(
+            "SELECT name, date FROM analysis"),
+        "SELECT ID FROM analysis");
+    assert.equal(
+        ext_bookmarks.get_select_id_query_string("COUNT analysis"),
+        "SELECT ID FROM analysis");
+    assert.equal(
+        ext_bookmarks.get_select_id_query_string("fInD analysis"),
+        "SELECT ID FROM analysis");
+});
+
+QUnit.test("select-query extraction", function (assert) {
+    // Use response field copied from demo
+    const response_field = $(`<div class="card caosdb-query-response mb-2">
+    <div class="card-header caosdb-query-response-heading">
+         <div class="row">
+              <div class="col-sm-10 caosdb-overflow-box">
+                   <div class="caosdb-overflow-content">
+                        <span>Query: </span>
+                        <span class = "caosdb-f-query-response-string">SELECT name, id FROM RECORD MusicalAnalysis</span>
+                   </div>
+              </div>
+              <div class="col-sm-2 text-end">
+                   <span>Results: </span>
+                   <span class="caosdb-query-response-results">3</span>
+              </div>
+         </div>
+    </div>
+</div>`);
+    $("body").append(response_field);
+
+    assert.equal(ext_bookmarks.get_query_from_response(),
+        "SELECT ID FROM RECORD MusicalAnalysis");
+});
\ No newline at end of file
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index ad26e8455fb7ca16a9e6e9687606b6aba1d0e7e6..d2ef27952e41142a62eb70e144571bc9d30c52d2 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -709,12 +709,6 @@ QUnit.test("removeAllErrorNotifications", function (assert) {
         '<div class="caosdb-preview-waiting-notification">Please wait!</div></div>')[0];
     let emptyElem = $('<div></div>')[0];
 
-    assert.throws(() => {
-        preview.removeAllErrorNotifications();
-    }, "no parameter throws");
-    assert.throws(() => {
-        preview.removeAllErrorNotifications(null);
-    }, "null parameter throws");
 
     assert.equal(okElem.childNodes.length, 3, "before: three children");
     assert.equal(okElem, preview.removeAllErrorNotifications(okElem), "return first parameter");
@@ -731,13 +725,6 @@ QUnit.test("removeAllWaitingNotifications", function (assert) {
         '<div class="caosdb-preview-error-notification">Error!</div></div>')[0];
     let emptyElem = $('<div></div>')[0];
 
-    assert.throws(() => {
-        removeAllWaitingNotifications();
-    }, "no parameter throws");
-    assert.throws(() => {
-        removeAllWaitingNotifications(null);
-    }, "null parameter throws");
-
     assert.equal(okElem.childNodes.length, 3, "before: three children");
     assert.equal(okElem, removeAllWaitingNotifications(okElem), "return first parameter");
     assert.equal(okElem.childNodes.length, 1, "after: one child");