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.

Qingyun Technology Community
Qingyun Technology Community
Qingyun Technology Community
How to Build a Powerful Advanced Search UI with Dynamic Filters

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendTypeScriptElasticsearchadvanced-searchdynamic operatorsfilter UI
Qingyun Technology Community
Written by

Qingyun Technology Community

Official account of the Qingyun Technology Community, focusing on tech innovation, supporting developers, and sharing knowledge. Born to Learn and Share!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.