Search customization
We need to allow the search interface to be customized through a config setting.
The main idea would be that SearchContext
would hold the information about all of the customization. Already we use a different SearchContext
for different parts of the app, so this should be fairly easy. In the nomad.yaml file I could envision we have a section for all the different search contexts. The default layout could be something like this:
search:
contexts:
include:
- "entries"
exclude: []
options:
entries:
path: "entries"
resource: "entries"
breadcrumb: "Entries search"
label: "Entries"
description: "Search individual database entries"
help:
title: "Entries search"
content: ...
pagination:
order_by: "upload_create_time"
order: "desc"
columns:
enable:
- "entry_name"
- "results.material.chemical_formula_hill"
- "entry_type"
- "upload_create_time"
- "authors"
include:
- "entry_name"
- "results.material.chemical_formula_hill"
- "entry_type"
- "results.method.method_name"
- "results.method.simulation.program_name"
- "results.method.simulation.dft.basis_set_name"
- "results.method.simulation.dft.xc_functional_type"
- "results.material.structural_type"
- "results.material.symmetry.crystal_system"
- "results.material.symmetry.space_group_symbol"
- "results.material.symmetry.space_group_number"
- "results.eln.lab_ids"
- "results.eln.sections"
- "results.eln.methods"
- "results.eln.tags"
- "results.eln.instruments"
- "mainfile"
- "upload_create_time"
- "authors"
- "comment"
- "references"
- "datasets"
- "published"
exclude: []
options:
- entry_name: {"label": "Name", "align": "left"}
- results.material.chemical_formula_hill: {"label": "Formula", "align": "left"}
- entry_type: {"label": "Entry type", "align": "left"}
- results.method.method_name: {}
- results.method.simulation.program_name: {}
- results.method.simulation.dft.basis_set_name: {}
- results.method.simulation.dft.xc_functional_type: {"label": "XC functional type"}
- results.material.structural_type: {}
- results.material.symmetry.crystal_system: {}
- results.material.symmetry.space_group_symbol: {}
- results.material.symmetry.space_group_number: {}
- results.eln.lab_ids: {}
- results.eln.sections: {}
- results.eln.methods: {}
- results.eln.tags: {}
- results.eln.instruments: {}
- mainfile: {"align": "left"}
- upload_create_time: {"label": "Upload time", "align": "left"}
- authors: {"align": "left"}
- comment: {"align": "left"}
- references: {"align": "left"}
- datasets: {"align": "left"}
- published: {"label": "Access"}
menus:
include:
- "material"
- "elements"
- "symmetry"
- "method"
- "simulation"
- "dft"
- "gw"
- "experiment"
- "eels"
- "properties"
- "electronic"
- "optoelectonic"
- "vibrational"
- "mechanical"
- "spectroscopy"
- "thermodynamic"
- "geometry_optimization"
- "eln"
- "author"
- "dataset"
- "access"
- "id"
- "processed_data_quantities"
- "optimade"
exclude: []
options:
material: {"label": "Material", "level": 0, "size": "small"}
elements: {"label": "Elements / Formula", "level": 1, "size": "large"}
symmetry: {"label": "Symmetry", "level": 1, "size": "small"}
method: {"label": "Method", "level": 0, "size": "small"}
simulation: {"label": "Simulation", "level": 1, "size": "small"}
dft: {"label": "DFT", "level": 2, "size": "small"}
gw: {"label": "GW", "level": 2, "size": "small"}
experiment: {"label": "Experiment", "level": 1, "size": "small"}
eels: {"label": "EELS", "level": 2, "size": "small"}
properties: {"label": "Properties", "level": 0, "size": "small"}
electronic: {"label": "Electronic", "level": 1, "size": "small"}
optoelectonic: {"label": "Optoelectronic", "level": 1, "size": "small"}
vibrational: {"label": "Vibrational", "level": 1, "size": "small"}
mechanical: {"label": "Mechanical", "level": 1, "size": "small"}
spectroscopy: {"label": "Spectroscopy", "level": 1, "size": "small"}
thermodynamic: {"label": "Thermodynamic", "level": 1, "size": "small"}
geometry_optimization: {"label": "Geometry Optimization", "level": 1, "size": "small"}
eln: {"label": "Electronic Lab Notebook", "level": 0, "size": "small"}
author: {"label": "Author / Origin", "level": 0, "size": "medium"}
dataset: {"label": "Dataset", "level": 0, "size": "small"}
access: {"label": "Access", "level": 0, "size": "small"}
id: {"label": "IDs", "level": 0, "size": "small"}
processed_data_quantities: {"label": "Processed Data Quantities", "level": 0, "size": "medium"}
optimade: {"label": "Optimade", "level": 0, "size": "medium"}
This kind of column config would quite easily allow several different config scenarios:
- Removing an unwanted option would be done with
exclude
. - Re-arranging options would be done by changing the
include
list. - Customizing the options done by overriding a key in
options
. - Completely new items by adding new entries in
options
.
The merging of existing config (a
) and new config (b
) is done by a simple recursive dict merge: new key/value pairs in b
are added to a
, conflicts are resolved so that if the value is not a dict, the existing value in a
is overwritten, otherwise the recursion continues. This would work really well with the suggested schema, as you can easily target changes even very deep in the config with very small amount of new config. E.g. consider the following config override that only changes the mentioned items and leaves other values to their defaults:
search:
contexts:
options:
entries:
columns:
enable:
- "entry_name"
options:
entry_name: {"label": "Name", "align": "left"}
menus:
include:
- "material"
- "elements"
- "symmetry"
- "eln"
- "author"
- "dataset"
- "access"
- "id"
- "processed_data_quantities"
- "optimade"
options:
material: {"label": "Material", "level": 0, "size": "small"}
Later down the line, we can simply extend the menu config to also contain the filters that are shown within them. There one could configure e.g. the order, the component used for displaying, the default statistics scaling etc. Also we could envision having a new section that enables/disables search filters for individual ES fields, as this also affects the search bar suggestions etc.