Search UI uses a configuration object to tailor search to your needs. It consists of three parts:

  • Search query
  • Autocomplete query
  • Event hooks

See below for details on each part.

See SearchProvider page for more information on how to use the configuration with the SearchProvider.

Search Query (QueryConfig)

This is the configuration for the main search query. Some of these configuration options are not supported by some connectors. Each connector will document the options that are not supported.


searchQuery: {
  filters: [{ field: "world_heritage_site", values: ["true"] }],
  facets: {
    states: { type: "value", size: 30 },
  }
  disjunctiveFacets: ["states"], // Array of field names to use for disjunctive searches
  disjunctiveFacetsAnalyticsTags: ["Ignore"],
  conditionalFacets: {},
  search_fields: {
    title: {},
    description: {}
  },
  result_fields: {
    title: {
      snippet: {
        size: 100,
        fallback: true
      }
    },
    nps_link: {
      raw: {}
    }
  }
}

Filters (Global Filters)

Using Query Config, it is possible to create "Global" filters. "Global filters" are filters that are added to every query. The user has no control over whether or not this filter is added or removed, it doesn't show up in the query string, and is completely transparent to the user. It is applied IN ADDITION to filters which the user applies.

filters: [
  // value filter example
  { field: "world_heritage_site", values: ["true"] },
  // Range filter example
  {
    field: "acres",
    values: [{
      from: 0, to: 1000, name: "Small"
    }]
  }
]

Facets

Tells Search UI to fetch facet data that can be used with Facet Components.

  facets: {
    // example of a value facet
    states: { type: "value", size: 30 },

    // example of a numeric range facet
    acres: {
      type: "range",
      ranges: [
        { from: -1, name: "Any" },
        { from: 0, to: 1000, name: "Small" },
        { from: 1001, to: 100000, name: "Medium" },
        { from: 100001, name: "Large" },
      ],
    },

    // example of a date range facet
    date_established: {
      type: "range",
      ranges: [
        {
          from: "1950-10-05T14:48:00.000Z",
          name: "Within the last 50 years",
        },
        {
          from: "1900-10-05T14:48:00.000Z",
          to: "1950-10-05T14:48:00.000Z",
          name: "50 - 100 years ago",
        },
        {
          to: "1920-10-05T14:48:00.000Z",
          name: "More than 100 years ago",
        },
      ],

      // example of a geo location range facet
      location: {
        // center location to base ranges off of
        center: "37.7749, -122.4194",
        type: "range",
        unit: "mi",
        ranges: [
          { from: 0, to: 100, name: "Nearby" },
          { from: 100, to: 500, name: "A longer drive" },
          { from: 500, name: "Perhaps fly?" },
        ],
      },
    }
  }

Disjunctive Faceting

"Disjunctive" facets are facets that do not change when a selection is made. Meaning, all available options will remain as selectable options even after a selection is made.

Configured in the searchQuery.disjunctiveFacets array. An array of field names. Every field listed here must have been configured in the facets field first. It denotes that a facet should be considered disjunctive. When returning counts for disjunctive facets, the counts will be returned as if no filter is applied on this field, even if one is applied.

disjunctiveFacetsAnalyticsTags

Used in conjunction with the disjunctiveFacets parameter. Adding disjunctiveFacets can cause additional API requests to be made to your API, which can create deceiving analytics. These queries will be tagged with "Facet-Only" by default. This field lets you specify a different tag for these.

Example, use ignore as a tag on all disjunctive API calls:

disjunctiveFacetsAnalyticsTags: ["ignore"];

Conditional Faceting

See Conditional Faceting for more information.

search_fields

Fields which should be searched with search term.

search_fields: {
  title: {
    weight: 10,
  },
  description: {},
  tags: {
    weight: 5,
  }
}

Apply Weights to each search field.

Engine level Weight settings will be applied is none are provided.

Query time Weights take precedence over Engine level values.

All fields specified within the search relevance section will be used for searching if not specified.

result_fields

Select from two ways to render text field values:

  • Raw: An exact representation of the value within a field. And it is exact! It is not HTML escaped.
  • Snippet: A snippet is an HTML escaped representation of the value within a field, where query matches are captured in <em> tags.

A raw field defaults to the full field with no character limit outside of max document size. A custom range must be at least 20 characters.

A snippet field defaults to 100 characters. A custom range must be between 20-1000 characters.

Only text fields provide these two options, as they are functions of the deep full-text search capabilities of App Search.

Raw

result_fields: {
  title: {
    raw: {}
  },
  description: {
    raw: {
      size: 50
    }
  }
}
fielddescription
size
Number - Optional. Length of the return value. Only can be used on text fields. Must be at least 20; defaults to the entire text field. If given for a different field type other than text, it will be silently ignored.

Snippet (Highlighting)

Requests a snippet of a text field.

The query match will be wrapped in tags, for highlighting, if a match exists.

Use escaped quotations to highlight only on exact, case insensitive matches.

Matches are HTML escaped prior to inserting tags. Fallbacks are also HTML escaped.

If requesting a snippet on a non-text field, the snippet field will be null.

If there is no match for your query, the snippet field will be null.

Snippets on an array value will return the first match or null. There is no fallback support.

On synonyms: If a search finds a synonym for a query, the synonym will not be highlighted.

For example, if "path" and "trail" are synonyms and a query is done for "path", the term "trail" will not be highlighted.

result_fields: {
  title: {
    snippet: {
      size: 100,
      fallback: true
    }
  },
  description: {
    raw: {
      size: 50
    }
  }
}
fielddescription
size
Character length of the snippet returned. Must be at least 20; defaults to 100.
fallback
If true, fallback to the raw field if no match is found.

Autocomplete Query

This is the configuration that provide relevant query suggestions for incomplete queries. Some of these configuration options are not supported by some connectors. Each connector will document the options that are not supported.

autocompleteQuery: {
  // performs a prefix search on the query
  results: {
    resultsPerPage: 5, // number of results to display. Default is 5.
    result_fields: {
      // Add snippet highlighting within autocomplete suggestions
      title: { snippet: { size: 100, fallback: true }},
      nps_link: { raw: {} }
    }
  },
  // performs a query to suggest for values that partially match the incomplete query
  suggestions: {
    types: {
      // Limit query to only suggest based on "title" field
      documents: {  fields: ["title"] }
    },
    // Limit the number of suggestions returned from the server
    size: 4
  }
}

Results

results will perform autocomplete on the query being typed. This will give back results that are relevant to the query before the user has typed any additional characters.

results: {
  resultsPerPage: 5,
  result_fields: {
    title: { snippet: { size: 100, fallback: true }},
    nps_link: { raw: {} }
  }
},
fielddescription
resultPerPage
Optional. Number type. Number of results suggested
result_fields
Optional. To specify the fields for each result hit. Use same configuration as result fields

Suggestions

Suggestions Query configuration for Search UI largely follows the same API as the App Search Search API.

{
  "types": {
    "documents": {
      "fields": ["title", "states"]
    }
  },
  "size": 4
}
optiontyperequired?source
types
Object
required
Object, keyed by "type" of query suggestion, with configuration for that type of suggestion.
size
Integer
optional
Number of suggestions to return.

Result Suggestions

Supported only by the Elasticsearch-connector

A different index can be used for the suggestions. Some examples:

  • Popular queries index from analytics
  • Brands index from product data
  • Categories index from product data

Below we are using the popular_queries index and performing a prefix match search on the query.suggest field. One thing to note, make sure the api-key has access to the index.

See Example retrieving suggestions from another index for more information.

autocompleteQuery: {
  suggestions: {
    popularQueries: {
      search_fields: {
        "query.suggest": {} // fields used to query
      },
      result_fields: {
        query: { // fields used for display
          raw: {}
        }
      },
      index: "popular_queries",
      queryType: "results"
    }
  }
}

Event Hooks

Search UI exposes a number of event hooks which need handlers to be implemented in order for Search UI to function properly.

The easiest way to provide handlers for these events is via an out-of-the-box "Connector", which provides pre-built handlers, which can then be configured for your particular use case.

While we do provide out-of-the-box Connectors, it is also possible to implement these handlers directly, override Connector methods, or provide "middleware" to Connectors in order to further customize how Search UI interacts with your services.

Event Handlers

methodparamsreturndescription
onResultClick
props - Object
This method logs a click-through event to your APIs analytics service. This is triggered when a user clicks on a result on a result page.
- query - String
The query used to generate the current results.
- documentId - String
The id of the result that a user clicked.
- requestId - String
A unique id that ties the click to a particular search request.
- tags - Array[String]
Tags used for analytics.
onSearch
queryConfig - Query Config
onAutocompleteResultClick
props - Object
This method logs a click-through event to your APIs analytics service. This is triggered when a user clicks on a result in an autocomplete dropdown
- query - String
The query used to generate the current results.
- documentId - String
The id of the result that a user clicked.
- requestId - String
A unique id that ties the click to a particular search request.
- tags - Array[String]
Tags used for analytics.
onAutocomplete
queryConfig - Object
- results - Query Config
If this is set, results should be returned for autocomplete.
- suggestions - Suggestions Query Config
If this is set, query suggestions should be returned for autocomplete.

Explicitly providing a Handler will override the Handler provided by the Connector.

<SearchProvider
  config={{
    apiConnector: connector,
    onSearch: async (requestState, queryConfig) => {
      const queryForOtherService = transformSearchUIStateToQuery(
        requestState,
        queryConfig
      );
      const otherServiceResponse = await callSomeOtherService(
        queryForOtherService
      );
      return transformOtherServiceResponseToSearchUIState(otherServiceResponse);
    }
  }}
/>

Using middleware in Connector Handlers

Handler implementations can also be used as middleware for Connectors by leveraging the next function.

<SearchProvider
  config={{
    apiConnector: connector,
    onSearch: (requestState, queryConfig, next) => {
      const updatedState = someStateTransformation(requestState);
      return next(updatedState, queryConfig);
    }
  }}
/>

Routing Options

Search UI provides a number of options for how to customise how state is serialised onto the URL. Within the config there is a routingOptions object which can be used to override the serialisation and parsing of the url to state.

Below is an example of how to use the routingOptions object to customise the url to be more SEO friendly.

This will result in the url pattern being like https://example.com/search/california,alaska?query=shoes

const routingOptions = {
  readUrl: () => {
    return asPath;
  },
  writeUrl: (url, { replaceUrl }) => {
    const method = router[replaceUrl ? "replace" : "push"];
    method(url);
  },
  stateToUrl: (state) => {
    const statesFilter = state.filters.find(
      (filter) => filter.field === "states"
    );
    const states = statesFilter ? statesFilter.values.join(",") : "all";
    return `/search/${states}?query=${state.searchTerm}`;
  },
  urlToState: (url) => {
    const match = url.match(/\/search\/(\w+)\?query=(\w+)/);
    if (!match) return {};
    return {
      searchTerm: match[2],
      filters: [{ field: "states", values: [match[1].split(",")], type: "any" }]
    };
  }
};

const config = {
  // search UI config
  routingOptions: routingOptions
};