diff --git a/gui/src/components/archive/ArchiveBrowser.js b/gui/src/components/archive/ArchiveBrowser.js
index 22fda995e9b477f90bb8fbf3cef761dd7f3199ef..6aa6b2da78941871f9f68a951cc24a655abb624f 100644
--- a/gui/src/components/archive/ArchiveBrowser.js
+++ b/gui/src/components/archive/ArchiveBrowser.js
@@ -829,6 +829,8 @@ const QuantityValue = React.memo(function QuantityValue({value, def, ...more}) {
       return <Compartment title='hdf5'>
         <H5Web upload_id={h5UploadId} filename={h5Path.groups.filename} initialPath={h5Path.groups.path} source={h5Source} sidebarOpen={false}></H5Web>
       </Compartment>
+    } else if (def?.type?.type_kind === 'custom' && def?.type?.type_data === 'nomad.datamodel.data.Query') {
+      return <Query value={value} def={def}/>
     } else {
       const [finalValue] = getRenderValue(value)
       return <Typography>{typeof finalValue === 'object' ? JSON.stringify(finalValue) : finalValue?.toString()}</Typography>
@@ -1628,6 +1630,31 @@ Quantity.propTypes = ({
   ])
 })
 
+function Query({value, def}) {
+  return <Content>
+    <Compartment title="filters">
+      <QuantityValue
+        value={value?.filters ? Object.entries(value.filters).map(([key, value]) => `${key}: ${value}`) : []}
+        def={def}
+      />
+    </Compartment>
+    {value?.data && <Compartment title="results">
+      <QuantityValue
+        value={value?.data ? value.data.map(entry => entry.mainfile || entry.entry_id) : []}
+        def={def}
+      />
+    </Compartment>}
+    <Compartment title="query">
+      <SourceApiCall body={value} response={value}/>
+    </Compartment>
+  </Content>
+}
+
+Query.propTypes = ({
+  value: PropTypes.any,
+  def: PropTypes.object.isRequired
+})
+
 function Attribute({value, def}) {
   return <Content>
     <ArchiveTitle def={def} data={value} kindLabel="attribute"/>
diff --git a/gui/src/components/editQuantity/EditQuantity.js b/gui/src/components/editQuantity/EditQuantity.js
index 58222c3ad02d2c886187ef783322ac2a8a50e568..b0522110d39f9ccd28394d73f0de3914aca28f1d 100644
--- a/gui/src/components/editQuantity/EditQuantity.js
+++ b/gui/src/components/editQuantity/EditQuantity.js
@@ -16,17 +16,18 @@
  * limitations under the License.
  */
 
-import {DateEditQuantity, DateTimeEditQuantity, TimeEditQuantity} from '../editQuantity/DateTimeEditQuantity'
-import { StringEditQuantity, URLEditQuantity } from '../editQuantity/StringEditQuantity'
-import {NumberEditQuantity} from '../editQuantity/NumberEditQuantity'
-import {EnumEditQuantity} from '../editQuantity/EnumEditQuantity'
-import {AutocompleteEditQuantity} from '../editQuantity/AutocompleteEditQuantity'
-import {BoolEditQuantity} from '../editQuantity/BoolEditQuantity'
-import FileEditQuantity from '../editQuantity/FileEditQuantity'
-import RichTextEditQuantity from '../editQuantity/RichTextEditQuantity'
-import ReferenceEditQuantity from '../editQuantity/ReferenceEditQuantity'
-import AuthorEditQuantity from '../editQuantity/AuthorEditQuantity'
-import { RadioEnumEditQuantity } from '../editQuantity/RadioEnumEditQuantity'
+import {DateEditQuantity, DateTimeEditQuantity, TimeEditQuantity} from './DateTimeEditQuantity'
+import { StringEditQuantity, URLEditQuantity } from './StringEditQuantity'
+import {NumberEditQuantity} from './NumberEditQuantity'
+import {EnumEditQuantity} from './EnumEditQuantity'
+import {AutocompleteEditQuantity} from './AutocompleteEditQuantity'
+import {BoolEditQuantity} from './BoolEditQuantity'
+import FileEditQuantity from './FileEditQuantity'
+import RichTextEditQuantity from './RichTextEditQuantity'
+import ReferenceEditQuantity from './ReferenceEditQuantity'
+import AuthorEditQuantity from './AuthorEditQuantity'
+import { RadioEnumEditQuantity } from './RadioEnumEditQuantity'
+import QueryEditQuantity from "./QueryEditQuantity"
 
 export const editQuantityComponents = {
   NumberEditQuantity: NumberEditQuantity,
@@ -43,5 +44,6 @@ export const editQuantityComponents = {
   TimeEditQuantity: TimeEditQuantity,
   RichTextEditQuantity: RichTextEditQuantity,
   ReferenceEditQuantity: ReferenceEditQuantity,
-  AuthorEditQuantity: AuthorEditQuantity
+  AuthorEditQuantity: AuthorEditQuantity,
+  QueryEditQuantity: QueryEditQuantity
 }
diff --git a/gui/src/components/editQuantity/EditQuantityExamples.js b/gui/src/components/editQuantity/EditQuantityExamples.js
index 9a0b730313212abbb8f54d4969024dc1b8478864..da89da51e0d019cec88d7f83d10dde4ba221dd8a 100644
--- a/gui/src/components/editQuantity/EditQuantityExamples.js
+++ b/gui/src/components/editQuantity/EditQuantityExamples.js
@@ -31,6 +31,7 @@ import ListEditQuantity from './ListEditQuantity'
 import { Code } from '../buttons/SourceDialogButton'
 import { stripIndent } from '../../utils'
 import AuthorEditQuantity from './AuthorEditQuantity'
+import QueryEditQuantity from "./QueryEditQuantity"
 
 const enumValues = [
   'Vapor deposition', 'Chemical vapor deposition', 'Metalorganic vapour phase epitaxy', 'Electrostatic spray assisted vapour deposition (ESAVD)', 'Sherardizing',
@@ -71,6 +72,7 @@ export function EditQuantityExamples() {
       propsRef.current[name] = {
         quantityDef: {
           name: name,
+          _qualifiedName: `${name}-qualifiedName`,
           description: `
             This is **MARKDOWN** help text.
           `,
@@ -440,6 +442,21 @@ export function EditQuantityExamples() {
                     <AuthorEditQuantity {...createDefaultProps('Author')} />
                   </Example>
                 </Grid>
+                <Grid item>
+                  <Example
+                    code={`
+                    query:
+                      type: Query
+                      m_annotations:
+                        eln:
+                          component: QueryEditQuantity`}
+                  >
+                    <QueryEditQuantity
+                      {...createDefaultProps('myQuery', {value: {}})}
+                      storeInArchive={true}
+                    />
+                  </Example>
+                </Grid>
               </Grid>
             </Box>
           </CardContent>
diff --git a/gui/src/components/editQuantity/QueryEditQuantity.js b/gui/src/components/editQuantity/QueryEditQuantity.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa2e5ce550e2c4483bbd06e482ad9607079d3736
--- /dev/null
+++ b/gui/src/components/editQuantity/QueryEditQuantity.js
@@ -0,0 +1,305 @@
+/*
+ * Copyright The NOMAD Authors.
+ *
+ * This file is part of NOMAD. See https://nomad-lab.eu for further info.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React, {useCallback, useEffect, useMemo, useState} from 'react'
+import PropTypes from 'prop-types'
+import {cloneDeep} from 'lodash'
+import {
+  Chip,
+  Dialog, DialogContent, IconButton, TextField, Tooltip
+} from "@material-ui/core"
+import {ManualSearchContext, useSearchContext} from "../search/SearchContext"
+import {ui} from "../../config"
+import DialogActions from "@material-ui/core/DialogActions"
+import Button from "@material-ui/core/Button"
+import SearchPage from "../search/SearchPage"
+import SearchIcon from "@material-ui/icons/Search"
+import {useRecoilValue} from "recoil"
+import {configState} from "../archive/ArchiveBrowser"
+import {getDisplayLabel, pluralize} from "../../utils"
+import Autocomplete from "@material-ui/lab/Autocomplete"
+import {ItemButton, useLane} from "../archive/Browser"
+import ClearIcon from "@material-ui/icons/Clear"
+
+const context = cloneDeep(ui?.apps?.options?.eln)
+
+const shownColumns = [
+  'entry_name',
+  'entry_type',
+  'authors',
+  'upload_name'
+]
+
+const columns = context?.columns
+const rows = context?.rows
+columns.selected = shownColumns
+rows.details = {enabled: false}
+rows.actions = {enabled: false}
+
+function SearchDialog({open, filters, pageSize, onCancel, onQueryChanged}) {
+  const {filters: queryFilters, useFilters, useSetFilters, useResults, useApiData} = useSearchContext()
+  const setFilters = useSetFilters()
+  const filterValues = useFilters(queryFilters)
+  const {data, setPagination} = useResults()
+  const apiData = useApiData()
+
+  const updateFilters = useCallback(() => {
+    const newValue = {}
+    for (const key in filters) {
+      if (Object.hasOwnProperty.call(filters, key)) {
+        newValue[key] = Array.isArray(filters[key]) || filters[key] instanceof Set ? new Set(filters[key]) : filters[key]
+      }
+    }
+    if (setPagination && pageSize) {
+      setPagination(old => {
+        return {...old, page_size: pageSize}
+      })
+    }
+    setFilters(newValue)
+  }, [pageSize, setFilters, filters, setPagination])
+
+  useEffect(() => {
+    if (open) {
+      updateFilters()
+    }
+  }, [updateFilters, open])
+
+  const newFilters = useMemo(() => {
+    const filters = {...filterValues}
+    for (const key in filters) {
+      if (key in filters && filters[key] === undefined) {
+        delete filters[key]
+      }
+    }
+    return filters
+  }, [filterValues])
+
+  const results = useMemo(() => {
+    if (!data) {
+      return undefined
+    }
+    return data.map(entry => ({entry_id: entry.entry_id, mainfile: entry.mainfile}))
+  }, [data])
+
+  const handleQueryChanged = useCallback(() => {
+    if (onQueryChanged) {
+      onQueryChanged(newFilters, apiData, results)
+    }
+  }, [onQueryChanged, newFilters, apiData, results])
+
+  return <Dialog
+    open={open}
+    PaperProps={{
+      style: {
+        maxWidth: '1800px',
+        maxHeight: '1200px',
+        width: '1800px',
+        height: '1200px'
+      }
+    }}
+    data-testid='search-dialog'
+  >
+    <DialogContent>
+      <SearchPage/>
+    </DialogContent>
+    <DialogActions>
+      <span style={{flexGrow: 1}} />
+      <Button onClick={() => onCancel()} color="secondary">
+        Cancel
+      </Button>
+      <Button onClick={() => handleQueryChanged()} color="secondary" data-testid='search-dialog-ok'>
+        OK
+      </Button>
+    </DialogActions>
+  </Dialog>
+}
+SearchDialog.propTypes = {
+  open: PropTypes.bool,
+  filters: PropTypes.object,
+  pageSize: PropTypes.number,
+  onCancel: PropTypes.func,
+  onQueryChanged: PropTypes.func
+}
+
+function QueryEditQuantity({quantityDef, onChange, value, storeInArchive, index, maxData}) {
+  const config = useRecoilValue(configState)
+  const label = getDisplayLabel(quantityDef, true, config?.showMeta)
+  const [open, setOpen] = useState(false)
+  const lane = useLane()
+
+  const filters = useMemo(() => value?.filters || {}, [value])
+
+  const handleCancel = useCallback(() => {
+    setOpen(false)
+  }, [])
+
+  const handleQueryChanged = useCallback((filters, query, results) => {
+    if (onChange) {
+      const newFilters = {}
+      for (const key in filters) {
+        if (Object.hasOwnProperty.call(filters, key)) {
+          newFilters[key] = Array.isArray(filters[key]) || filters[key] instanceof Set ? [...filters[key]] : filters[key]
+        }
+      }
+      const newValue = {
+        owner: query.response.owner,
+        query: query.response.query,
+        pagination: query.response.pagination,
+        filters: newFilters
+      }
+      if (storeInArchive) {
+        newValue.data = results
+      }
+      onChange(newValue)
+    }
+    setOpen(false)
+  }, [onChange, storeInArchive])
+
+  const tags = useMemo(() => {
+    let tags = []
+    for (const key in filters) {
+      const filterValue = filters[key]
+      if (filterValue) {
+        if (Array.isArray(filterValue[key])) {
+          tags = tags.concat([...filterValue].map(value => ({key: key, value: value, tag: `${key}:${value}`})))
+        } else {
+          tags = tags.concat({key: key, value: filterValue, tag: `${key}:${filterValue}`})
+        }
+      }
+    }
+    return tags
+  }, [filters])
+
+  const itemKey = useMemo(() => {
+    if (!isNaN(index)) {
+      return `${quantityDef.name}:${index}`
+    } else {
+      return quantityDef.name
+    }
+  }, [quantityDef, index])
+
+  const handleClearResults = useCallback(() => {
+    if (onChange) {
+      onChange(undefined)
+    }
+  }, [onChange])
+
+  const actions = useMemo(() => {
+    const actions = []
+    if (value) {
+      actions.push(
+        <IconButton
+          key={'clear'}
+          color="seconadry"
+          size="small"
+          onClick={handleClearResults}
+        >
+          <Tooltip title="Clear results">
+            <ClearIcon/>
+          </Tooltip>
+        </IconButton>
+      )
+    }
+    actions.push(<IconButton
+      key={'search'}
+      color="seconadry"
+      size="small"
+      onClick={() => setOpen(true)}
+    >
+      <Tooltip title="Search dialog">
+        <SearchIcon/>
+      </Tooltip>
+    </IconButton>)
+    if (lane && value) {
+      actions.push(<ItemButton key={'navigate'} size="small" itemKey={itemKey}/>)
+    }
+    return actions
+  }, [value, lane, handleClearResults, itemKey])
+
+  return <React.Fragment>
+    <Autocomplete
+      multiple
+      open={false}
+      options={[]}
+      getOptionLabel={(option) => option.tag}
+      limitTags={1}
+      value={tags}
+      inputValue={''}
+      renderTags={(value, getTagProps) => {
+        return value.map((option, index) => {
+          return <Chip
+            key={index}
+            {...getTagProps({ index })}
+            label={option.tag}
+            size="small"
+            color="primary"
+            onDelete={undefined}
+          />
+        })
+      }}
+      renderInput={(params) => (
+        <TextField
+          {...params}
+          label={label}
+          variant="filled"
+          placeholder={value?.results && Array.isArray(value.results) && value.results.length > 0
+            ? pluralize('result', value.results.length, true)
+            : "no results"}
+          InputProps={{
+            ...params.InputProps,
+            endAdornment: React.cloneElement(params.InputProps.endAdornment, {}, actions)
+          }}
+        />
+      )}
+    />
+    <ManualSearchContext
+      resource={context?.resource}
+      initialPagination={context?.pagination}
+      initialColumns={columns}
+      initialRows={rows}
+      initialFilterMenus={context?.filter_menus}
+      initialFiltersLocked={undefined}
+      initialFilterValues={filters}
+      initialSearchSyntaxes={context?.search_syntaxes}
+      id={`queryeditquantity-${quantityDef._qualifiedName}`}
+    >
+      <SearchDialog
+        open={open}
+        filters={filters}
+        onCancel={handleCancel}
+        onQueryChanged={handleQueryChanged}
+        pageSize={maxData || 100}
+      />
+    </ManualSearchContext>
+  </React.Fragment>
+}
+QueryEditQuantity.propTypes = {
+  // The quantity definition
+  quantityDef: PropTypes.object,
+  // The event when the searched results have been changed
+  onChange: PropTypes.func,
+  // The searched value
+  value: PropTypes.string,
+  // To store the search results in the value
+  storeInArchive: PropTypes.bool,
+  // The index of the quantity which repeats
+  index: PropTypes.number,
+  // The maximum number of searched data
+  maxData: PropTypes.number
+}
+
+export default QueryEditQuantity
diff --git a/gui/src/components/editQuantity/QueryEditQuantity.spec.js b/gui/src/components/editQuantity/QueryEditQuantity.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..dd55a9cfa0263cad17721f10762edc1fe62ae286
--- /dev/null
+++ b/gui/src/components/editQuantity/QueryEditQuantity.spec.js
@@ -0,0 +1,104 @@
+/*
+ * Copyright The NOMAD Authors.
+ *
+ * This file is part of NOMAD. See https://nomad-lab.eu for further info.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react'
+import {closeAPI, render, screen, startAPI, waitForGUI} from '../conftest.spec'
+import QueryEditQuantity from "./QueryEditQuantity"
+import {waitFor, within} from "@testing-library/dom"
+import {act} from "react-dom/test-utils"
+import userEvent from '@testing-library/user-event'
+
+const handleChange = jest.fn(value => {})
+const quantityDef = {
+  name: 'myQuery',
+  _qualifiedName: `myQuery-qualifiedName`,
+  description: `
+            This is **MARKDOWN** help text.
+          `
+}
+
+const testSearchDialogCancelButton = async () => {
+  const dialog = screen.getByTestId('search-dialog')
+  await waitFor(() => expect(screen.queryByText('visibility=visible')).toBeInTheDocument())
+
+  // cancel the search
+  await userEvent.click(within(dialog).getByRole('button', {name: /cancel/i}))
+  await waitFor(() => expect(screen.queryByTestId('search-dialog')).not.toBeInTheDocument())
+}
+
+const testSearchDialogOkButton = async () => {
+  const dialog = screen.getByTestId('search-dialog')
+  await waitFor(() => expect(screen.queryByText('visibility=visible')).toBeInTheDocument())
+
+  // accept the search
+  await userEvent.click(within(dialog).getByTestId('search-dialog-ok'))
+  await waitFor(() => expect(screen.queryByTestId('search-dialog')).not.toBeInTheDocument())
+}
+
+test('Test QueryEditQuantity', async () => {
+  await startAPI('tests.states.entry.eln', 'tests/data/editquantity/query', 'test', 'password')
+  render(<QueryEditQuantity
+    quantityDef={quantityDef}
+    storeInArchive={true}
+    value={{
+      filters: {visibility: 'visible'},
+      results: [
+        {entry_id: '1', mainfile: 'a'},
+        {entry_id: '2', mainfile: 'b'},
+        {entry_id: '3', mainfile: 'c'}
+      ]}}
+    onChange={handleChange}
+  />)
+
+  const input = screen.getByRole('textbox')
+  expect(input.value).toBe('')
+
+  screen.queryByText('visibility:visible')
+  screen.queryByText('3 results')
+
+  const searchDialogButton = screen.getByTitle('Search dialog').closest('button')
+  expect(searchDialogButton).toBeEnabled()
+
+  await act(async () => { userEvent.click(searchDialogButton) })
+  await waitForGUI(1000, true)
+
+  await testSearchDialogCancelButton()
+  screen.getByText('visibility:visible')
+  expect(input).toHaveAttribute('placeholder', '3 results')
+
+  await act(async () => { userEvent.click(searchDialogButton) })
+  await waitForGUI(1000, true)
+
+  await testSearchDialogOkButton()
+  screen.getByText('visibility:visible')
+
+  // Assert the new results
+  await waitFor(() => expect(handleChange.mock.calls[0][0].data[0].entry_id).toBe('bC7byHvWJp62Sn9uiuJUB38MT5j-'))
+  await waitFor(() => expect(handleChange.mock.calls[0][0].data[0].mainfile).toBe('sample.archive.json'))
+  await waitFor(() => expect(handleChange.mock.calls[0][0].data[1].entry_id).toBe('83DS7AzwqTKFVwlrdVeaL3kMSLU_'))
+  await waitFor(() => expect(handleChange.mock.calls[0][0].data[1].mainfile).toBe('schema.archive.yaml'))
+
+  // Clear results
+  const clearResultsButton = screen.getByTitle('Clear results').closest('button')
+  expect(clearResultsButton).toBeEnabled()
+  await act(async () => { userEvent.click(clearResultsButton) })
+
+  await waitFor(() => expect(handleChange.mock.calls[1][0]).toBe(undefined))
+
+  closeAPI()
+})
diff --git a/gui/src/components/search/SearchContext.js b/gui/src/components/search/SearchContext.js
index 3a34a0e0a36bad672bd07aa61ad4477dbfa81d4b..f7333a8aa21a239694d50099631c15027f07ea24 100644
--- a/gui/src/components/search/SearchContext.js
+++ b/gui/src/components/search/SearchContext.js
@@ -1806,6 +1806,15 @@ export function useSearchContext() {
   return useContext(searchContext)
 }
 
+export const ManualSearchContext = withFilters(SearchContextRaw)
+
+/**
+ * Hook to control the current SearchContext manually.
+ */
+export function useManualSearchContext() {
+  return useContext(ManualSearchContext)
+}
+
 /**
  * Parses a single filter value into a form that is supported by the GUI. This includes:
  * - Arrays are are transformed into Sets
diff --git a/gui/tests/data/editquantity/query.json b/gui/tests/data/editquantity/query.json
new file mode 100644
index 0000000000000000000000000000000000000000..fefdd8088923b9fba69b30233530c809ad8ee589
--- /dev/null
+++ b/gui/tests/data/editquantity/query.json
@@ -0,0 +1,495 @@
+{
+  "afa783d98e040c74b68e5cfd3d82f3f0": [
+    {
+      "request": {
+        "url": "http://localhost:8000/fairdi/nomad/latest/api/v1/entries/query",
+        "method": "POST",
+        "body": {
+          "owner": "visible",
+          "query": {},
+          "aggregations": {},
+          "pagination": {
+            "order_by": "upload_create_time",
+            "order": "desc",
+            "page_size": 20
+          },
+          "required": {
+            "exclude": [
+              "quantities",
+              "sections",
+              "files"
+            ]
+          }
+        },
+        "headers": {
+          "accept": "application/json, text/plain, */*",
+          "content-type": "application/json",
+          "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJSWFFIV1YxSEJ6cmh5U3h3UmRDdkhCcUF1WVNKRzZWSEJSZXg0TW5oX293In0.eyJleHAiOjE3MTkwMTA0MTMsImlhdCI6MTcxODk3NDQyNiwianRpIjoiMWE5ZDcyMjItYzI0Ni00NjdjLTgyZTUtYTMyMTBlMjVmZTIxIiwiaXNzIjoiaHR0cHM6Ly9ub21hZC1sYWIuZXUvZmFpcmRpL2tleWNsb2FrL2F1dGgvcmVhbG1zL2ZhaXJkaV9ub21hZF90ZXN0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjY4ODc4YWY3LTY4NDUtNDZjMC1iMmMxLTI1MGQ0ZDhlYjQ3MCIsInR5cCI6IkJlYXJlciIsImF6cCI6Im5vbWFkX2d1aV9kZXYiLCJzZXNzaW9uX3N0YXRlIjoiZDIyNjc5MzUtNTZiNS00NzU5LWFlNzktOTA0NTg3Yjg2NDdiIiwiYWxsb3dlZC1vcmlnaW5zIjpbIioiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUiLCJzaWQiOiJkMjI2NzkzNS01NmI1LTQ3NTktYWU3OS05MDQ1ODdiODY0N2IiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6Ik1hcmt1cyBTY2hlaWRnZW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ0ZXN0IiwiZ2l2ZW5fbmFtZSI6Ik1hcmt1cyIsImZhbWlseV9uYW1lIjoiU2NoZWlkZ2VuIiwiZW1haWwiOiJtYXJrdXMuc2NoZWlkZ2VuQGZoaS1iZXJsaW4uZGUifQ.hiDY95D_2Ti4UXmirl88lIiPjVtLOddaP9QyGLdqDE2OM2jgnMT7QdnjBpBARrhVAuq78OOinwGDqXnh4gPzlIF4AEeLw06vFSpWqoKOKY5WN9I25jqXzC6AvsfDGmQKRSQfxeULFH4g1KbYYBE9GWArBvtkLq6BwkMYMDUB-Qz3MC5YcrA1sBIhKYQO3zvN2Mt5q7RuTWQqcS_ueLVQ9WhIpoDFumHBxWA1XHSHL8NxcxF4enku1SkEXeOfw26FEyruMMFx6cxm_htrqbkdkeNwf8WqfLIhPgrO6J4Sm-OIt1YiyqpdnVFyt8e_Wabm6qQ3SGBMrR2DSdVof_sJWg",
+          "cookie": null
+        }
+      },
+      "response": {
+        "status": 200,
+        "body": {
+          "owner": "visible",
+          "query": {},
+          "pagination": {
+            "page_size": 20,
+            "order_by": "upload_create_time",
+            "order": "desc",
+            "total": 2
+          },
+          "required": {
+            "exclude": [
+              "quantities",
+              "sections",
+              "files"
+            ]
+          },
+          "data": [
+            {
+              "upload_id": "eln_upload_id",
+              "references": [],
+              "origin": "Markus Scheidgen",
+              "text_search_contents": [
+                "ELN example sample",
+                "001",
+                "PVDProcess.csv",
+                "<p>A simple example for an \"sample\" that demonstrates how to combine different data entities.</p>\n<p>The sample it-self defines a few properties (involved chemicals, used substrate) and uses inherited default properties (formula, name, lab id, ...)</p>\n<p>But the sample also contains sub-sections that prodivde inforamtion about proccessed that were applied to this sample (PVD evaporation, hotplate annealing).&nbsp;</p>\n<p>The sample also show references to other entries (chemicals, instruments).",
+                "SLG",
+                "project"
+              ],
+              "datasets": [],
+              "n_quantities": 68,
+              "nomad_version": "1.3.3.dev67+g6dbdb317b",
+              "upload_create_time": "2024-06-21T12:53:41.020000+00:00",
+              "nomad_commit": "",
+              "section_defs": [
+                {
+                  "used_directly": true,
+                  "definition_id": "7e0331d2df29682413535f00d4aad156783fee2e",
+                  "definition_qualified_name": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Process"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "030133d881170f0d9e31da6086ed5641f0c4403c",
+                  "definition_qualified_name": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "f0086b987424b1001666c6f5df9ad1071164a27e",
+                  "definition_qualified_name": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "d39f4397900226398e6c17eb00467e874e5955c4",
+                  "definition_qualified_name": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes.HotplateAnnealing"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "c640c9585e8d4549f248582763427a7dd7c417a0",
+                  "definition_qualified_name": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes.PvdEvaporation"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "7047cbff9980abff17cce4b1b6b0d1c783505b7f",
+                  "definition_qualified_name": "nomad.datamodel.data.ArchiveSection"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "538f52fd8d52b29372066f878319c6aeb03b74d2",
+                  "definition_qualified_name": "nomad.datamodel.data.EntryData"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "1067a7855c86bc74d57bfbe3c95e7b5ca97cc403",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.EntryArchive"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "08e24de48352035bcc932ed9934875ed9b10188e",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.EntryMetadata"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "265ae33dbc9ace82b50260c1050d30c59ef9cb3c",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.RFC3161Timestamp"
+                },
+                {
+                  "used_directly": false,
+                  "definition_id": "fc2735d177bf36f9718ca66a764a56fc0c6200a0",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.Activity"
+                },
+                {
+                  "used_directly": false,
+                  "definition_id": "add2edfa25a61ff3bbfdebacc870181f64f41634",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.BaseSection"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "c7254c9b461a4baec86cb3179e2f84513f5c9053",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.CompositeSystem"
+                },
+                {
+                  "used_directly": false,
+                  "definition_id": "7ce6bdcaa183a9685582b275e1c2b2ea3139c74d",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.Entity"
+                },
+                {
+                  "used_directly": false,
+                  "definition_id": "8d81e1f95c60f39a6a8c9f954143a55e6f4c984c",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.Process"
+                },
+                {
+                  "used_directly": false,
+                  "definition_id": "3ab3a09d615ab58aefbaa3acdd8d6af4e73aaae5",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.basesections.System"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "55ef08f33dcf9fb374aea217458bd34a23bec57b",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.plot.Figure"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "7bb17e35ed91dca88bf6238fc51ed212693593d0",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.plot.PlotSection"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "491cdc3c5ea21cdc576275a5c1e600cdaa4256c1",
+                  "definition_qualified_name": "nomad.datamodel.metainfo.plot.PlotlyFigure"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "eeed330bd18d40f3a2a00cc35ba6f58808d0c543",
+                  "definition_qualified_name": "nomad.datamodel.results.ELN"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "f6d62b2664a0c37e0c82d4cb570e5e177e445e05",
+                  "definition_qualified_name": "nomad.datamodel.results.Properties"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "2820e376005366caae1a7662ad21cfacefc34abb",
+                  "definition_qualified_name": "nomad.datamodel.results.Results"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "4408b02aafbcd5a196e33346021b9da4e64a84e8",
+                  "definition_qualified_name": "nomad.parsing.tabular.TableData"
+                }
+              ],
+              "processing_errors": [],
+              "results": {
+                "eln": {
+                  "names": [
+                    "ELN example sample"
+                  ],
+                  "methods": [
+                    "PvdEvaporation",
+                    "HotplateAnnealing"
+                  ],
+                  "descriptions": [
+                    "<p>A simple example for an \"sample\" that demonstrates how to combine different data entities.</p>\n<p>The sample it-self defines a few properties (involved chemicals, used substrate) and uses inherited default properties (formula, name, lab id, ...)</p>\n<p>But the sample also contains sub-sections that prodivde inforamtion about proccessed that were applied to this sample (PVD evaporation, hotplate annealing).&nbsp;</p>\n<p>The sample also show references to other entries (chemicals, instruments).</p>\n<p>&nbsp;</p>"
+                  ],
+                  "sections": [
+                    "PvdEvaporation",
+                    "HotplateAnnealing",
+                    "Sample"
+                  ],
+                  "lab_ids": [
+                    "001"
+                  ],
+                  "tags": [
+                    "project"
+                  ]
+                },
+                "properties": {
+                  "available_properties": []
+                }
+              },
+              "entry_name": "ELN example sample",
+              "last_processing_time": "2024-06-21T12:53:41.238000+00:00",
+              "parser_name": "parsers/archive",
+              "calc_id": "bC7byHvWJp62Sn9uiuJUB38MT5j-",
+              "published": false,
+              "writers": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                }
+              ],
+              "writer_groups": [],
+              "processed": true,
+              "mainfile": "sample.archive.json",
+              "main_author": {
+                "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                "name": "Markus Scheidgen"
+              },
+              "viewers": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                },
+                {
+                  "user_id": "54cb1f64-f84e-4815-9ade-440ce0b5430f",
+                  "name": "Test Tester"
+                }
+              ],
+              "viewer_groups": [],
+              "entry_create_time": "2024-06-21T12:53:41.108000+00:00",
+              "with_embargo": false,
+              "search_quantities": [
+                {
+                  "path_archive": "data.name",
+                  "str_value": "ELN example sample",
+                  "definition": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.name",
+                  "id": "data.name#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.lab_id",
+                  "str_value": "001",
+                  "definition": "nomad.datamodel.metainfo.basesections.BaseSection.lab_id",
+                  "id": "data.lab_id#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.description",
+                  "str_value": "<p>A simple example for an \"sample\" that demonstrates how to combine different data entities.</p>\n<p>The sample it-self defines a few properties (involved chemicals, used substrate) and uses inherited default properties (formula, name, lab id, ...)</p>\n<p>But the sample also contains sub-sections that prodivde inforamtion about proccessed that were applied to this sample (PVD evaporation, hotplate annealing).&nbsp;</p>\n<p>The sample also show references to other entries (chemicals, instruments).</p>\n<p>&nbsp;</p>",
+                  "definition": "nomad.datamodel.metainfo.basesections.BaseSection.description",
+                  "id": "data.description#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.substrate_type",
+                  "str_value": "SLG",
+                  "definition": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.substrate_type",
+                  "id": "data.substrate_type#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.pvd_evaporation.datetime",
+                  "datetime_value": "2022-05-10T07:20:00+00:00",
+                  "definition": "nomad.datamodel.metainfo.basesections.Activity.datetime",
+                  "id": "data.processes.pvd_evaporation.datetime#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.pvd_evaporation.data_file",
+                  "str_value": "PVDProcess.csv",
+                  "definition": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes.PvdEvaporation.data_file",
+                  "id": "data.processes.pvd_evaporation.data_file#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.pvd_evaporation.fill_archive_from_datafile",
+                  "bool_value": false,
+                  "definition": "nomad.parsing.tabular.TableData.fill_archive_from_datafile",
+                  "id": "data.processes.pvd_evaporation.fill_archive_from_datafile#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.hotplate_annealing.datetime",
+                  "datetime_value": "2022-05-10T07:22:00+00:00",
+                  "definition": "nomad.datamodel.metainfo.basesections.Activity.datetime",
+                  "id": "data.processes.hotplate_annealing.datetime#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.hotplate_annealing.set_temperature",
+                  "definition": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes.HotplateAnnealing.set_temperature",
+                  "float_value": 373.15,
+                  "id": "data.processes.hotplate_annealing.set_temperature#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.processes.hotplate_annealing.duration",
+                  "definition": "entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample.Processes.HotplateAnnealing.duration",
+                  "float_value": 60,
+                  "id": "data.processes.hotplate_annealing.duration#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                },
+                {
+                  "path_archive": "data.datetime",
+                  "datetime_value": "2024-06-21T14:53:41.294628+00:00",
+                  "definition": "nomad.datamodel.metainfo.basesections.BaseSection.datetime",
+                  "id": "data.datetime#entry_id:83DS7AzwqTKFVwlrdVeaL3kMSLU_.Sample"
+                }
+              ],
+              "entry_type": "Sample",
+              "entry_id": "bC7byHvWJp62Sn9uiuJUB38MT5j-",
+              "authors": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                }
+              ],
+              "license": "CC BY 4.0",
+              "data": {
+                "name": "ELN example sample",
+                "lab_id": "001",
+                "description": "<p>A simple example for an \"sample\" that demonstrates how to combine different data entities.</p>\n<p>The sample it-self defines a few properties (involved chemicals, used substrate) and uses inherited default properties (formula, name, lab id, ...)</p>\n<p>But the sample also contains sub-sections that prodivde inforamtion about proccessed that were applied to this sample (PVD evaporation, hotplate annealing).&nbsp;</p>\n<p>The sample also show references to other entries (chemicals, instruments).</p>\n<p>&nbsp;</p>",
+                "substrate_type": "SLG",
+                "processes": {
+                  "pvd_evaporation": {
+                    "datetime": "2022-05-10T07:20:00+00:00",
+                    "data_file": "PVDProcess.csv",
+                    "fill_archive_from_datafile": false
+                  },
+                  "hotplate_annealing": {
+                    "datetime": "2022-05-10T07:22:00+00:00",
+                    "set_temperature": 373.15,
+                    "duration": 60
+                  }
+                },
+                "datetime": "2024-06-21T14:53:41.294628+00:00"
+              }
+            },
+            {
+              "upload_id": "eln_upload_id",
+              "references": [],
+              "origin": "Markus Scheidgen",
+              "text_search_contents": [],
+              "datasets": [],
+              "n_quantities": 155,
+              "nomad_version": "1.3.3.dev67+g6dbdb317b",
+              "upload_create_time": "2024-06-21T12:53:41.020000+00:00",
+              "nomad_commit": "",
+              "section_defs": [
+                {
+                  "used_directly": true,
+                  "definition_id": "7047cbff9980abff17cce4b1b6b0d1c783505b7f",
+                  "definition_qualified_name": "nomad.datamodel.data.ArchiveSection"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "1067a7855c86bc74d57bfbe3c95e7b5ca97cc403",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.EntryArchive"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "08e24de48352035bcc932ed9934875ed9b10188e",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.EntryMetadata"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "265ae33dbc9ace82b50260c1050d30c59ef9cb3c",
+                  "definition_qualified_name": "nomad.datamodel.datamodel.RFC3161Timestamp"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "f6d62b2664a0c37e0c82d4cb570e5e177e445e05",
+                  "definition_qualified_name": "nomad.datamodel.results.Properties"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "2820e376005366caae1a7662ad21cfacefc34abb",
+                  "definition_qualified_name": "nomad.datamodel.results.Results"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "f6617f133366ffb8cd1bde4bf643ca5bae1db7f8",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.Definition"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "0c6464110f52fa3f556ae8e95e2f0272e201525a",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.Package"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "28f841a88474d266eded1141318ea4c42d3aff3f",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.Property"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "d12b9e6f4aed521dc6feeb67239a929542f78b8d",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.Quantity"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "f293173ca54be29603fb12b9b138f8a76130a25c",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.Section"
+                },
+                {
+                  "used_directly": true,
+                  "definition_id": "19bf6b5bf2ffa69e6585a1fb1c6b0961d4a89738",
+                  "definition_qualified_name": "nomad.metainfo.metainfo.SubSection"
+                }
+              ],
+              "processing_errors": [],
+              "results": {
+                "properties": {
+                  "available_properties": []
+                }
+              },
+              "entry_name": "Electronic Lab Notebook example schema",
+              "last_processing_time": "2024-06-21T12:53:41.242000+00:00",
+              "parser_name": "parsers/archive",
+              "calc_id": "83DS7AzwqTKFVwlrdVeaL3kMSLU_",
+              "published": false,
+              "writers": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                }
+              ],
+              "writer_groups": [],
+              "processed": true,
+              "mainfile": "schema.archive.yaml",
+              "main_author": {
+                "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                "name": "Markus Scheidgen"
+              },
+              "viewers": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                },
+                {
+                  "user_id": "54cb1f64-f84e-4815-9ade-440ce0b5430f",
+                  "name": "Test Tester"
+                }
+              ],
+              "viewer_groups": [],
+              "entry_create_time": "2024-06-21T12:53:41.111000+00:00",
+              "with_embargo": false,
+              "entry_type": "Schema",
+              "entry_id": "83DS7AzwqTKFVwlrdVeaL3kMSLU_",
+              "authors": [
+                {
+                  "user_id": "68878af7-6845-46c0-b2c1-250d4d8eb470",
+                  "name": "Markus Scheidgen"
+                },
+                {
+                  "user_id": "a03af8b6-3aa7-428a-b3b1-4a6317e576b6",
+                  "name": "Sheldon Cooper"
+                }
+              ],
+              "license": "CC BY 4.0"
+            }
+          ]
+        },
+        "headers": {
+          "connection": "close",
+          "content-length": "18373",
+          "content-type": "application/json",
+          "server": "uvicorn"
+        }
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/nomad/datamodel/data.py b/nomad/datamodel/data.py
index 4c9d27f43350011551f5b2fc92f13edbf5a56b9a..6fcfd9080d6d3e8a3110a70a450358dc5ff6fc95 100644
--- a/nomad/datamodel/data.py
+++ b/nomad/datamodel/data.py
@@ -20,6 +20,9 @@ import os.path
 
 from cachetools import TTLCache, cached
 
+from typing import Dict, Any, Optional
+from pydantic import Field
+
 from nomad.config import config
 from nomad.metainfo.elasticsearch_extension import Elasticsearch, material_entry_type
 from nomad.metainfo.metainfo import (
@@ -32,6 +35,7 @@ from nomad.metainfo.metainfo import (
     Section,
     Datetime,
     Reference,
+    JSON,
 )
 from nomad.metainfo.pydantic_extension import PydanticModel
 
@@ -230,4 +234,25 @@ class AuthorReference(Reference):
 
 
 author_reference = AuthorReference()
+
+
+class Query(JSON):
+    """
+    To represent a search query, including the applied filters and the results.
+
+    filters : dict
+        A dictionary of filters applied to the search. Keys are filter names, and values are the filter values.
+    query : MetadataResponse
+        A dictionary of the used query in the current search.
+    """
+
+    def _normalize_impl(self, value, **kwargs):
+        from nomad.app.v1.models import MetadataResponse
+
+        class QueryResult(MetadataResponse):
+            filters: Optional[Dict[str, Any]] = Field(None)
+
+        return QueryResult().parse_obj(value).dict()
+
+
 Schema = EntryData
diff --git a/nomad/datamodel/metainfo/annotations.py b/nomad/datamodel/metainfo/annotations.py
index 6599c26aab59e01a3272c84ab9e7a27859c635cc..8d10975aaab5cf3d76c56db87118880a46803966 100644
--- a/nomad/datamodel/metainfo/annotations.py
+++ b/nomad/datamodel/metainfo/annotations.py
@@ -26,6 +26,7 @@ from pydantic.main import BaseModel
 from nomad.utils import strip
 from nomad.metainfo import AnnotationModel, MEnum, MTypes, Datetime, Reference, Quantity
 from .plot import PlotlyError
+from ..data import Query
 from ...metainfo.data_type import Datatype
 
 
@@ -46,6 +47,7 @@ class ELNComponentEnum(str, Enum):
     ReferenceEditQuantity = 'ReferenceEditQuantity'
     UserEditQuantity = 'UserEditQuantity'
     AuthorEditQuantity = 'AuthorEditQuantity'
+    QueryEditQuantity = 'QueryEditQuantity'
 
 
 valid_eln_types = {
@@ -65,6 +67,7 @@ valid_eln_types = {
     'user': ['User'],
     'author': ['Author'],
     'reference': [''],
+    'query': ['Query'],
 }
 
 
@@ -94,6 +97,7 @@ valid_eln_components = {
     'user': [ELNComponentEnum.AuthorEditQuantity],
     'author': [ELNComponentEnum.AuthorEditQuantity],
     'reference': [ELNComponentEnum.ReferenceEditQuantity],
+    'query': [ELNComponentEnum.QueryEditQuantity],
 }
 
 
@@ -437,6 +441,10 @@ class ELNAnnotation(AnnotationModel):
                 )
             elif type_.standard_type().startswith('enum'):
                 assert_component(component, name, 'enum', valid_eln_components['enum'])
+            elif isinstance(type_, Query):
+                assert_component(
+                    component, name, type(type_).__name__, valid_eln_components['query']
+                )
         elif isinstance(type_, type):
             if type_.__name__ == 'str':
                 assert_component(
@@ -454,6 +462,10 @@ class ELNAnnotation(AnnotationModel):
                 assert_component(
                     component, name, type_.__name__, valid_eln_components['author']
                 )
+            elif type_.__name__ == 'Query':
+                assert_component(
+                    component, name, type_.__name__, valid_eln_components['query']
+                )
 
         elif type_ == Datetime:
             assert_component(
diff --git a/nomad/metainfo/data_type.py b/nomad/metainfo/data_type.py
index 801097f1f8028e39b1b4fbcb2577a1cf12612fc0..282a0dda20a71ff5a2e27b6d0f7724529f03dcd8 100644
--- a/nomad/metainfo/data_type.py
+++ b/nomad/metainfo/data_type.py
@@ -1055,6 +1055,11 @@ def normalize_type(value):
         if value.endswith('json'):
             return JSON()
 
+        if value.endswith('query'):
+            from nomad.datamodel.data import Query
+
+            return Query()
+
         if value.endswith('datetime'):
             return Datetime()
 
diff --git a/tests/datamodel/test_schema.py b/tests/datamodel/test_schema.py
index 549cfa85bc7a85d8d59087d972614a4ef2a6fe56..d67ee3b0a29a1588052eb53fd57601da38bda9cb 100644
--- a/tests/datamodel/test_schema.py
+++ b/tests/datamodel/test_schema.py
@@ -22,7 +22,7 @@ import pytest
 from nomad.metainfo import MetainfoError
 from nomad.datamodel.context import ServerContext
 from nomad.datamodel.datamodel import EntryArchive, EntryMetadata
-from nomad.datamodel.data import UserReference, AuthorReference
+from nomad.datamodel.data import UserReference, AuthorReference, Query
 from nomad.datamodel.metainfo.annotations import valid_eln_types, valid_eln_components
 from nomad.metainfo.data_type import Datatype
 from nomad.parsing.parser import ArchiveParser
@@ -162,3 +162,27 @@ def test_user_author_yaml_deserialization():
     assert des_my_author.name == 'my_author'
     assert isinstance(des_my_user.type, UserReference)
     assert isinstance(des_my_author.type, AuthorReference)
+
+
+def test_query_yaml_deserialization():
+    des_m_package = yaml_to_package(
+        strip(
+            """
+        m_def: 'nomad.metainfo.metainfo.Package'
+        sections:
+            Sample:
+                base_section: 'nomad.datamodel.metainfo.measurements.Sample'
+                quantities:
+                    my_query:
+                        type: Query
+                        m_annotations:
+                            eln:
+                                component: QueryEditQuantity
+    """
+        )
+    )
+    des_sample = des_m_package['section_definitions'][0]
+    des_my_query = des_sample.quantities[0]
+
+    assert des_my_query.name == 'my_query'
+    assert isinstance(des_my_query.type, Query)