How to Build a Powerful Advanced Search UI with Dynamic Filters
This article explains the design and step‑by‑step implementation of an advanced search interface that lets users configure multiple conditions, choose operators based on field types, and define logical relationships, ultimately generating a FilterConfig that can be sent to Elasticsearch for precise data retrieval.
Search is a frequently used function for quickly retrieving target data, but simple searches become insufficient when data volume grows and more precise criteria are needed, prompting the need for an "advanced search".
The design concept treats a search query as a collection of conditions, each composed of a field, an operator, and a value, allowing users to configure each condition independently while considering details such as differing value types and logical relationships (must vs. should).
The resulting UI is illustrated below:
Implementation details start with the TypeScript type definitions for a condition and its surrounding configuration:
type Condition = {<br/> key: string; // field key<br/> op: string; // operator<br/> value: FormValue; // value type from form component<br/>}<br/><br/>type FilterTag = 'should' | 'must';<br/><br/>type FilterConfig = {<br/> condition: Condition[];<br/> tag: FilterTag;<br/>}Each condition corresponds to a Condition, the FilterTag indicates whether all conditions must match (must) or any can match (should), and the final output is a FilterConfig.
Condition Configuration Implementation
Creating a FilterConfig involves five steps:
1. Add an empty condition
2. Choose the form field to use as a search condition
The first dropdown lets you select a form field, which can be a custom field or a system field:
Form fields are displayed as shown:
3. Select the operator
Operators vary by field type (String, Number, Date, Boolean, Array, LabelValue). The following constant arrays define the available operators:
const OPERATORS_STRING = [<br/> { label: '等于', value: 'eq' },<br/> { label: '模糊', value: 'like' },<br/>];<br/><br/>const OPERATORS_NUMBER = [<br/> { label: '等于', value: 'eq' },<br/> { label: '大于', value: 'gt' },<br/> { label: '小于', value: 'lt' },<br/> { label: '大于等于', value: 'gte' },<br/> { label: '小于等于', value: 'lte' },<br/> { label: '不等于', value: 'ne' },<br/>];<br/><br/>const OPERATORS_DATE = [<br/> { label: '范围', value: 'range' },<br/>];<br/><br/>const OPERATORS_BOOL = [<br/> { label: '等于', value: 'eq' },<br/> { label: '不等于', value: 'ne' },<br/>];<br/><br/>const OPERATORS_ARRAY_MULTIPLE = [<br/> { label: '同时包含', value: 'fullSubset' },<br/> { label: '包含任一一个', value: 'intersection' },<br/>];<br/><br/>const OPERATORS_LABEL_VALUE = [<br/> { label: '等于', value: 'eq' },<br/> { label: '不等于', value: 'ne' },<br/>];A helper function returns the appropriate operator list based on the field type:
function getOperators(type: string) {<br/> switch (type) {<br/> case 'array': return OPERATORS_ARRAY_MULTIPLE;<br/> case 'number': return OPERATORS_NUMBER;<br/> case 'datetime': return OPERATORS_DATE;<br/> case 'boolean': return OPERATORS_BOOL;<br/> case 'label-value': return OPERATORS_LABEL_VALUE;<br/> default: return OPERATORS_STRING;<br/> }<br/>}4. Enter the value for each condition
The input component adapts to the field type; for example, a NumberPicker restricts input to the defined numeric range.
The rendering logic for the value input is:
function FieldSwitch(type) {<br/> switch (type) {<br/> case 'CheckboxGroup':<br/> case 'Select':<br/> case 'RadioGroup':<br/> case 'MultipleSelect':<br/> return (<SelectField />);<br/> case 'NumberPicker':<br/> return (<InputNumber />);<br/> case 'DatePicker':<br/> return (<DatePicker.RangePicker />);<br/> case 'CascadeSelector':<br/> return (<CascadeSelector />);<br/> case 'OrganizationPicker':<br/> return (<OrganizationPicker />);<br/> case 'UserPicker':<br/> return (<UserPicker />);<br/> default:<br/> return <Input />;<br/> }<br/>}5. Define the logical relationship between conditions
Users choose either "All" (AND, must) or "Any" (OR, should) to control how conditions combine:
After configuring the UI, clicking search generates a FilterConfig, which is then transformed and sent to the backend (Elasticsearch) for execution. The conversion from FilterConfig to Elasticsearch’s AST will be covered in a future article.
In summary, the advanced search solves the limitations of simple search by allowing configurable operators, value types, and logical relationships, dramatically improving data retrieval efficiency.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qingyun Technology Community
Official account of the Qingyun Technology Community, focusing on tech innovation, supporting developers, and sharing knowledge. Born to Learn and Share!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
