diff --git a/gui/src/components/search/FilterContext.js b/gui/src/components/search/FilterContext.js
index 875b8cf4f09c8bb4f537acea49499208cafe104f..f70a5fb479cc121ced1b9498f03bb8f7a5c56d0e 100644
--- a/gui/src/components/search/FilterContext.js
+++ b/gui/src/components/search/FilterContext.js
@@ -140,6 +140,7 @@ registerQuantity('results.material.structural_type', labelMaterial, 'terms')
 registerQuantity('results.material.functional_type', labelMaterial, 'terms')
 registerQuantity('results.material.compound_type', labelMaterial, 'terms')
 registerQuantity('results.material.material_name', labelMaterial)
+registerQuantity('results.material.elements', labelElements, 'terms')
 registerQuantity('results.material.chemical_formula_hill', labelElements)
 registerQuantity('results.material.chemical_formula_anonymous', labelElements)
 registerQuantity('results.material.n_elements', labelElements, 'min_max', undefined, false)
@@ -171,13 +172,6 @@ registerQuantity('upload_id', labelIDs)
 registerQuantity('results.material.material_id', labelIDs)
 registerQuantity('datasets.dataset_id', labelIDs)
 
-// In regular element query we use the 'all'-postfix.
-registerQuantity(
-  'results.material.elements',
-  labelElements,
-  'terms',
-  { set: (query, value) => (query['results.material.elements:all'] = value) }
-)
 // In exclusive element query the elements names are sorted and concatenated
 // into a single string.
 registerQuantity(
@@ -238,6 +232,17 @@ registerQuantity(
   },
   false
 )
+// Restricted: controls whether materiarls search is done in a restricted mode.
+registerQuantity(
+  'restricted',
+  'Restricted',
+  undefined,
+  {
+    set: () => {},
+    get: () => {}
+  },
+  false
+)
 
 // Material and entry queries target slightly different fields. Here we prebuild
 // the mapping.
@@ -415,6 +420,7 @@ export function useResetFilters() {
     for (let filter of quantities) {
       reset(queryFamily(filter))
     }
+    reset(exclusive)
   }, [])
   return reset
 }
@@ -557,13 +563,14 @@ export function useUpdateQueryString() {
  * datatypes that are directly compatible with the filter components.
  */
 function qsToQuery(queryString) {
-  const query = qs.parse(queryString, { comma: true })
+  const query = qs.parse(queryString, {comma: true})
   const newQuery = {}
   for (let [key, value] of Object.entries(query)) {
     const split = key.split(':')
     key = split[0]
     let newKey = quantityFullnames.get(key) || key
-    const {type, parser} = parseMeta(newKey)
+    let multiple = quantityData[newKey].multiple
+    const {parser} = parseMeta(newKey)
     if (split.length !== 1) {
       const op = split[1]
       const oldValue = newQuery[newKey]
@@ -574,7 +581,7 @@ function qsToQuery(queryString) {
       }
     } else {
       if (isArray(value)) {
-        value = new Set(value)
+        value = new Set(value.map(parser))
       } else if (isPlainObject(value)) {
         if (!isNil(value.gte)) {
           value.gte = parser(value.gte)
@@ -584,7 +591,7 @@ function qsToQuery(queryString) {
         }
       } else {
         value = parser(value)
-        if (type !== 'number' && type !== 'timestamp' && key !== 'visibility') {
+        if (multiple) {
           value = new Set([value])
         }
       }
@@ -663,13 +670,12 @@ export function useAgg(quantity, restrict = false, update = true, delay = 500) {
   const [results, setResults] = useState(undefined)
   const initialAggs = useRecoilValue(initialAggsState)
   const query = useQuery()
-  const exclusive = useExclusive()
   const firstLoad = useRef(true)
 
   // Pretty much all of the required pre-processing etc. should be done in this
   // function, as it is the final one that gets called after the debounce
   // interval.
-  const apiCall = useCallback((query, exclusive) => {
+  const apiCall = useCallback((query) => {
     // If the restrict option is enabled, the filters targeting the specified
     // quantity will be removed. This way all possible options pre-selection can
     // be returned.
@@ -677,7 +683,7 @@ export function useAgg(quantity, restrict = false, update = true, delay = 500) {
     if (restrict && query && quantity in query) {
       delete queryCleaned[quantity]
     }
-    queryCleaned = toAPIQuery(queryCleaned, resource)
+    queryCleaned = toAPIQuery(queryCleaned, resource, query.restricted)
     const aggRequest = {}
     toAPIAgg(aggRequest, quantity, resource)
     const search = {
@@ -713,12 +719,12 @@ export function useAgg(quantity, restrict = false, update = true, delay = 500) {
       // Make an immediate request for the aggregation values if query has been
       // specified.
       } else {
-        apiCall(query, exclusive)
+        apiCall(query)
       }
     } else {
-      debounced(query, exclusive)
+      debounced(query)
     }
-  }, [apiCall, quantity, debounced, query, exclusive, update, initialAggs])
+  }, [apiCall, quantity, debounced, query, update, initialAggs])
 
   return results
 }
@@ -752,9 +758,10 @@ export function useScrollResults(pageSize, orderBy, order, exclusive, delay = 50
   // The results are fetched as a side effect in order to not block the
   // rendering. This causes two renders: first one without the data, the second
   // one with the data.
-  const apiCall = useCallback((query, pageSize, orderBy, order, exclusive) => {
+  const apiCall = useCallback((query, pageSize, orderBy, order) => {
     pageAfterValue.current = undefined
-    const cleanedQuery = toAPIQuery(query, resource)
+    const restricted = query.restricted
+    const cleanedQuery = toAPIQuery(query, resource, restricted)
     const search = {
       owner: query.visibility || 'visible',
       query: cleanedQuery,
@@ -814,12 +821,12 @@ export function useScrollResults(pageSize, orderBy, order, exclusive, delay = 50
       return
     }
     if (firstRender.current) {
-      apiCall(query, pageSize, orderBy, order, exclusive)
+      apiCall(query, pageSize, orderBy, order)
       firstRender.current = false
     } else {
-      debounced(query, pageSize, orderBy, order, exclusive)
+      debounced(query, pageSize, orderBy, order)
     }
-  }, [apiCall, debounced, query, exclusive, pageSize, order, orderBy])
+  }, [apiCall, debounced, query, pageSize, order, orderBy])
 
   // Whenever the ordering changes, we perform a single API call that fetches
   // results in the new order. The amount of fetched results is based on the
@@ -846,7 +853,7 @@ export function useScrollResults(pageSize, orderBy, order, exclusive, delay = 50
  * @returns {object} A copy of the object with certain items cleaned into a
  * format that is supported by the API.
  */
-export function toAPIQuery(query, resource) {
+export function toAPIQuery(query, resource, restricted) {
   // Perform custom transformations
   let queryCustomized = {}
   for (let [k, v] of Object.entries(query)) {
@@ -858,10 +865,10 @@ export function toAPIQuery(query, resource) {
     }
   }
 
-  // Transform sets into lists and Quantities into SI values and modify keys
-  // according to target resource (entries/materials).
   let queryNormalized = {}
-  for (let [k, v] of Object.entries(queryCustomized)) {
+  for (const [k, v] of Object.entries(queryCustomized)) {
+    // Transform sets into lists and Quantities into SI values and modify keys
+    // according to target resource (entries/materials).
     let newValue
     if (isPlainObject(v)) {
       newValue = {}
@@ -874,8 +881,61 @@ export function toAPIQuery(query, resource) {
     } else {
       newValue = toAPIQueryValue(v)
     }
-    k = resource === 'materials' ? quantityMaterialNames[k.split(':')[0]] : k
-    queryNormalized[k] = newValue
+
+    // The query key postfixes and key remapping is done here. By default query
+    // items with array values get the 'any'-postfix.
+    let postfix
+    if (isArray(v)) {
+      const quantityPostfixMap = {
+        'results.properties.available_properties': 'all',
+        'results.material.elements': 'all'
+      }
+      postfix = quantityPostfixMap[k] || 'any'
+    }
+
+    // For material query the keys are remapped.
+    let newKey = resource === 'materials' ? quantityMaterialNames[k] : k
+    newKey = postfix ? `${newKey}:${postfix}` : newKey
+    queryNormalized[newKey] = newValue
+  }
+
+  if (resource === 'materials') {
+    // In restricted search we simply move all method/properties filters
+    // inside a single entries-subsection.
+    if (restricted) {
+      const entrySearch = {}
+      for (const [k, v] of Object.entries(queryNormalized)) {
+        if (k.startsWith('entries.')) {
+          const name = k.split('entries.').pop()
+          entrySearch[name] = v
+          delete queryNormalized[k]
+        }
+      }
+      if (!isEmpty(entrySearch)) {
+        queryNormalized.entries = entrySearch
+      }
+    // In unrestricted search we have to split each filter and each filter value
+    // into it's own separate entries query. These queries are then joined with
+    // 'and'.
+    } else {
+      const entrySearch = []
+      for (const [k, v] of Object.entries(queryNormalized)) {
+        if (k.startsWith('entries.')) {
+          const newKey = k.split(':')[0]
+          if (isArray(v)) {
+            for (const item of v) {
+              entrySearch.push({[newKey]: item})
+            }
+          } else {
+            entrySearch.push({[newKey]: v})
+          }
+          delete queryNormalized[k]
+        }
+      }
+      if (entrySearch.length > 0) {
+        queryNormalized.and = entrySearch
+      }
+    }
   }
 
   return queryNormalized
diff --git a/gui/src/components/search/input/InputSlider.js b/gui/src/components/search/input/InputSlider.js
index 356303c7f4a29a6a46f8bfe9d54fac1b37783cf4..dc9ae0d7df8c171b9229bc6910776bfe95c82ec1 100644
--- a/gui/src/components/search/input/InputSlider.js
+++ b/gui/src/components/search/input/InputSlider.js
@@ -144,15 +144,10 @@ const InputSlider = React.memo(({
         min = minGlobalSI
         max = maxGlobalSI
       } else {
-        if (filter instanceof Quantity) {
-          gte = filter.toSI()
-          lte = filter.toSI()
-        } else {
-          gte = filter.gte ? filter.gte.toSI() : minGlobalSI
-          lte = filter.lte ? filter.lte.toSI() : maxGlobalSI
-        }
-        min = Math.min(gte, minGlobalSI)
-        max = Math.max(lte, maxGlobalSI)
+        gte = filter.gte instanceof Quantity ? filter.gte.toSI() : filter.gte
+        lte = filter.lte instanceof Quantity ? filter.lte.toSI() : filter.lte
+        min = isNil(gte) ? minGlobalSI : Math.min(gte, minGlobalSI)
+        max = isNil(lte) ? maxGlobalSI : Math.max(lte, maxGlobalSI)
       }
       setMinLocal(min)
       setMaxLocal(max)
diff --git a/gui/src/components/search/menus/FilterMainMenu.js b/gui/src/components/search/menus/FilterMainMenu.js
index 0647536d38bd9f044afe8bee38ba2a0503c07cca..adc8882336bc4783482579752890c930f304e0c5 100644
--- a/gui/src/components/search/menus/FilterMainMenu.js
+++ b/gui/src/components/search/menus/FilterMainMenu.js
@@ -23,7 +23,7 @@ import {
   FilterMenuItems,
   FilterSubMenus
 } from './FilterMenu'
-
+import { makeStyles } from '@material-ui/core/styles'
 import FilterSubMenuMaterial from './FilterSubMenuMaterial'
 import FilterSubMenuElements from './FilterSubMenuElements'
 import FilterSubMenuSymmetry from './FilterSubMenuSymmetry'
@@ -51,13 +51,20 @@ import {
   labelAuthor,
   labelDataset,
   labelIDs,
-  labelAccess
+  labelAccess,
+  useSearchContext
 } from '../FilterContext'
+import InputCheckbox from '../input/InputCheckbox'
 
 /**
  * Swipable menu that shows the available filters on the left side of the
  * screen.
  */
+const useStyles = makeStyles(theme => ({
+  restricted: {
+    paddingLeft: theme.spacing(2)
+  }
+}))
 const FilterMainMenu = React.memo(({
   open,
   onOpenChange,
@@ -67,6 +74,8 @@ const FilterMainMenu = React.memo(({
   onResultTypeChange
 }) => {
   const [value, setValue] = React.useState()
+  const {resource} = useSearchContext()
+  const styles = useStyles()
 
   return <FilterMenu
     selected={value}
@@ -91,6 +100,15 @@ const FilterMainMenu = React.memo(({
       <FilterMenuItem value={labelDataset} depth={0}/>
       <FilterMenuItem value={labelAccess} depth={0}/>
       <FilterMenuItem value={labelIDs} depth={0}/>
+      {resource === 'materials' &&
+        <InputCheckbox
+          quantity="restricted"
+          label="Restricted"
+          description="If selected, the query will return materials that have individual calculations simultaneously matching your methodology and properties criteria."
+          initialValue={true}
+          className={styles.restricted}
+        ></InputCheckbox>
+      }
     </FilterMenuItems>
     <FilterSubMenus>
       <FilterSubMenuMaterial value={labelMaterial}/>
diff --git a/gui/src/utils.js b/gui/src/utils.js
index 4efcc30c84902237487be6bd82b42a58bb9e5efb..d3a3befd042d0e85547a0742977db82d2bb18964 100644
--- a/gui/src/utils.js
+++ b/gui/src/utils.js
@@ -479,7 +479,16 @@ export function parseMeta(quantity, pretty = true) {
     }
   } else {
     type = 'unknown'
-    parser = (value) => value
+    parser = (value) => {
+      const keywords = {
+        true: true,
+        false: false
+      }
+      if (value in keywords) {
+        return keywords[value]
+      }
+      return value
+    }
   }
   return {type, parser}
 }