• UX for AI
  • Posts
  • Improve the Usability of Search-Results Pages: Add Sophisticated but Easy-to-Use Filtering and Sorting Controls

Improve the Usability of Search-Results Pages: Add Sophisticated but Easy-to-Use Filtering and Sorting Controls

E.R. Tufte, in his phenomenal book Envisioning Information, states, “Clarity and simplicity are completely opposite of simple-mindedness.” This false simple-mindedness is often evident in the design of a search-results page. Even on some of the leading e-commerce sites, this important page is frequently made hard to use by excessive visual clutter or the complete absence of appropriate sorting and filtering controls. This is especially daunting as more and more raw data return as search results, without the appropriate tools to manipulate them.

A well-designed search-results page is well worth the effort, since it is the key to helping your users successfully achieve their goals and enticing them back to your site. The engineering challenge is to provide just the right kind of sophisticated yet easy-to-use sorting and filtering tools that map well to your customers’ goals and mental models. In this article, I present some design ideas to start you on your way to creating a more usable search-results page.

Blood, sweat socks, and chicken soup

According to usability research, while most consumers search occasionally, more then half of all users are “search dominant,” meaning they go straight for the search and ignore the rest of the navigation on the site (see Designing Web Usability: The Practice of Simplicity). Thus, the search-results page is used by a vast majority of the customers, and the usability of this page is often key to the usability of your entire site. Given this data, it is astonishing how many companies ignore the usability design of this important functionality and are content to let their users muddle through.

Nowhere is this more evident than in e-commerce, as the following tale of Internet shopping illustrates: My wife has a well-concealed passion for cooking, but loves cookbooks about chocolate. For her birthday, I wanted to present her with a well-illustrated tour guide to chocolate cooking nirvana, and I figured this guide should range somewhere between 5 and 0. I knew exactly what I wanted, so I bravely set my browser to Amazon.com and typed “Chocolate Cookbook” in the site’s search function. Would you believe that (at the time of this writing) I found 14,597 books?

The first item was listed for .99, which fell outside my desired price range. The next two items had no visible price at all. Navigating to the next search-results page seemed fruitless: at 10 items per page, it would take 1,459 pages to see all 14,597 items. At a brisk browsing pace of 1 minute per page, this browsing process would take just more than 24 hours!

Sorting by “Avg. Customer Review” netted a very highly rated A Tale of Blood and Sweatsocks and a disappointedly chocolate-free Chicken Soup for the Teenage Soul (shown in Figure 1):

Figure 1. Sorting chocolate-cookbook search results by Avg. Customer Review. Click on thumbnail to view full-sized image.

It is possible that the search engine mistook the unfortunately named Chicken Soup series for a real cookbook, but a tale about sweat socks seemed completely out of place. At that point, I was completely stuck: Not only was there an excessive number of items in the search results, but most of the items seemed to be either in the wrong category or in the wrong price range. Most importantly, and absolutely critical to enable me, the user, to reach my chocolate shopping goals, there did not seem to be a way to filter the search results in such a way as to narrow them down from 14,597.

Too much data

My unfortunate shopping experience is indicative of the larger problem affecting many large consumer sites: excessive amounts of raw search-results data with no way to manipulate the results. Between the Herculean efforts to give customers “something” from their searches and brilliant marketing initiatives to maximize the number of “impulse buys,” many Web companies have lost track of their core competency: helping customers quickly find exactly what they are looking for.

Companies ignore usability of key functions at their peril. In his excellent book Don’t Make Me Think!, Steve Krug quotes his wife’s excellent statement on the key importance of usability: “If something is hard to use, I just don’t use it as much.” Anyone who has had a negative and frustrating experience with a Website will hesitate to use it again. Search programs can and should be made smarter (think Google), but the usability of the search-results page can also be improved dramatically.

The lack of appropriate controls to filter search results by category and price range made it impossible for me to purchase my cookbook at Amazon.com. To paraphrase the opening quote, engineers at Amazon.com substituted “simple-mindedness” for “clarity and simplicity.” What Tufte brilliantly wrote of information design is equally true of interaction design: “…the operating moral premise of information design should be that our readers are alert and caring; they may be busy, eager to get on with it, but they are not stupid.”

To keep users coming back to your site, all of the pages essential to the successful shopping experience must be well designed to support your user’s goals. One of the most frequently used pages is the search-results page, and it is well-worth the additional design effort. The challenge is to provide just the right kind of sophisticated sorting and filtering tools that are easy to use and map well to your users’ goals and mental models. As discussed in the next section, providing “just the right tools” is no easy task.

The right toolkit: Everything users need and nothing more

As a great designer Milton Glaser once famously said: “Less is not necessarily more…just enough is more.” Following this advice is by no means easy. To provide “just enough” functionality, a designer must focus directly on the goals of one particular user, and understand that user’s specific needs and behaviors extremely well (see About Face 2.0).

In my specific case, I knew exactly what I wanted, and my goals were clear:

  • Media type: Book

  • Category: Cookbook

  • Subject: Chocolate/deserts

  • Price range: 5 to 0

  • Rating: Highest customer rating and/or bestseller

To help me achieve my goals, Amazon provided a good sorting functionality for sorting the result set by user ratings or bestseller. The important feature missing from the Amazon search results was filtering, which compromised the result set’s usability. To improve the site usability, in light of my specific goals, Amazon could have introduced two simple filters: category and price range.

Either one of these filters would have dramatically decreased the number of search results. Together, these filters could have helped me zero-in on the manageable 20 to 50 chocolate cookbooks to examine in a reasonable amount of time. Having these filters would have allowed me to take control of the searching process and complete the buying task successfully.

It is worth noting that filtering itself is a foreign concept to most users. For this reason, Krug suggests that good search filter design should use a customer’s own terms and natural language, so the filter reads like a sentence. For example:

“Search ____ for ___________ in ___________ Price from $___ to $___”

Implementation is shown in Figure 2.

Figure 2. Implementing a natural language filtering scheme using HTML controls. Click on thumbnail to view full-sized image.

Good search filters are challenging controls to design. The filters in Figure 2 would definitely increase the task completion success rate in my particular use case, because they help me zero-in on the right product category in my price range. However, would these filters help all users in all cases? Would additional controls confuse newbie Web users and add clutter to the screen unnecessarily? Simple guesswork won’t give you the right answers to these questions. To design search filters correctly, you need to know your target users intimately.

The concept of a “persona” introduced by Alan Cooper in About Face 2.0 is a powerful and sophisticated tool for user behavior modeling. Personas are not real people, but they are based on the behaviors and motivations of real people gathered through usability research. Cooper defines a persona as “a precise descriptive model of the user, what he wishes to accomplish and why.” This model of a fictional archetypical user comes in handy for understanding user goals and exploring the ranges of behavior in specific contexts—such as your system’s interface.

Using personas allows the designer to prioritize which users are the most important ones to design for—those models become one or two “primary personas” for your project. Other important classes of users become “secondary personas.” Thus, the key to a successful “just-enough” design is to address the needs of the primary personas, without unduly inconveniencing the secondary personas. (For more discussion about personas as an effective design tool, see Resources for links to Cooper’s excellent books About Face 2.0 and The Inmates Are Running the Asylum.)

Regardless of whether you choose to use persona-modeling in your design, it is invaluable to conduct frequent usability tests with real users throughout the design process. The usability tests should be conducted frequently, and as early in the design process as possible, using static HTML or even low-tech paper prototypes. It may initially seem like a hassle, but if you consider the high cost of modification and quality assurance of the finished software product versus the low cost of modifying a paper prototype, obviously performing detailed product design before coding begins is well worth the effort. Persona-based design coupled with frequent usability testing is the most cost-effective way of ensuring that your final product will be successful in helping your users reach their goals.

Although filters are hard to design, the good news is they are often relatively easy to implement, which you will see as we add the category and price filter to my chocolate-cookbook search-results page.

Designing a category filter with drop-down control

One popular implementation of the category filter is a drop-down control. This implementation is especially handy if many categories need to be displayed and screen real estate is tight. Note that providing this filter up-front often makes little sense. Currently, Amazon has 35 categories, and few people will ever make the effort to read all 35 categories to find out which one fits the subject best. Most likely, if users wanted to put in the time to learn the categories, they would browse instead of search.

The key to a successful category filter is instead to narrow the categories only to those constrained by the search results, so the user can more easily choose the most likely category from the smaller, more relevant list. Usually, the category with the most items contains the most relevant search results, so it makes sense to show it first, with other relevant categories appearing in the order of descending item count. To make this ordering scheme clear to the user, it helps to show the item count after the category name. In our case, “Cooking” would clearly be the first category with the most items, making for easy user selection. For someone looking for cookbooks, it would be obvious to select “Cooking” and avoid the “Persperational Fiction” category, as shown in Figure 2.

Search filters can be implemented using SQL in the following way. Assuming the original search query used to retrieve “chocolate” items was:

FROM item
WHERE title = 'chocolate'  -- simplified for clarity
ORDER BY average_customer_review DESC

The list of categories for these search results can be obtained by using the original SQL search condition of title = 'chocolate', modified with a GROUP BY clause:

 SELECT category_name, category_id, COUNT(*) AS items_count
FROM item
WHERE title = 'chocolate'
GROUP BY category_name, category_id

To construct the filter, we first need to create a small helper class, HtmlOption. Each object of this class represents one option in the drop-down (HTML select) control:

 //Value objects must be serializable for use in distributed Java systems like J2EE
public final class HtmlOption implements java.io.Serializable {

  //Both value and text are strings
  private final String val;
  private final String text;

  public HtmlOption(String text, String val) {
    this.text = text;
    this.val = val;

  //Accessors only, with no corresponding mutators
  public String getValue() {
    return val;

  public String getText() {
    return text;


Each object of this class will be immutable, which is a good object-oriented design practice and has many advantages. As Joshua Bloch writes in his famous book Effective Java, immutable objects are simple, have only one state, can be shared freely, and are inherently thread-safe.

Now we have everything we need to write the code that will generate our category filter:

 ResultSet rs; //Standard JDBC, the rest omitted for clarity

ArrayList categoriesFilter = new ArrayList();

//Add the first option: "All  Categories"
String text = "All Categories";
String val = "0";
categoriesFilter.add(new HtmlOption(text,val)); //Add the default no-filter option

while(rs.next()) {
  text = rs.getString("category_name") + " (" + rs.getString("items_count") + ")";
  val = rs.getString("category_id");
  categoriesFilter.add(new HtmlOption(text,val));

After this method runs, we will end up with an ArrayList categoriesFilter full of the custom HtmlSelect objects that represent the options in the HTML select control. Under a typical Model-View-Controller framework, the completed categoriesFilter object can be sent to a JSP (JavaServer Pages) page or Apache Velocity template to render the category filter as a simple HTML drop-down control. One simple way to do this is to use the following Velocity macro:

 #macro(renderHtmlSelectOptions $optionsList $selectedValue)
  #foreach($htmlOption in $optionsList)
    #if($selectedValue == $htmlOption.Value)
      <option value="$htmlOption.Value" SELECTED>$htmlOption.Text</option>
      <option value="$htmlOption.Value">$htmlOption.Text</option>

Note that in the context of Velocity, we can safely use the == operator to compare Java String objects. (For more information about Apache Velocity, see Resources.) To render the category filter, the above macro is called as follows from the Velocity template:

 <select name="categoryid" size="1">
  #renderHtmlSelectOptions($categoriesFilter, $gridViewRequest.CategoryId)

Note that Velocity will conveniently map the shorthand attribute notation $gridViewRequest.CategoryId to the corresponding getter method call $gridViewRequest.getCategoryId() according to the standard JavaBeans convention. At runtime, assuming none of the categories have been selected, the above macro will yield:

 <select name="categoryid">
  <option value="0" SELECTED>All Categories </option>
  <option value="2">Cooking (2,234)</option>
  <option value="5">Persirational Fiction (1,987)</option>
  <option value="8">History (1,385)</option>
  <option value="34">Agriculture (998)</option>

It is easy to see how immediately useful such filtering control would be to users—they can just select from the list of the categories relevant to their search, without learning all 35 categories on your site.

Users often find the interface to be less confusing if the order of the categories in the drop-down menu always remains in the same “relevance order” (highest items_count first), regardless of how the items in the grid are sorted. Of course, how you sort the categories in your particular filter will depend entirely upon what your users are trying to accomplish.

Designing minimum and maximum price filters

In Figure 2 above, the item price-range filter is prefilled with the available maximum and minimum prices of the range of items in the search results, providing both a good visual clue to how the control operates and a good starting point for the user’s exploration. Prefilling appropriate filter controls with a range of valid values is a nice touch and fairly easy to implement. Maximum and minimum prices for the particular search results can be obtained similarly to the categories above:

 SELECT MAX(price) AS max_price, MIN(price) AS min_price
FROM item
WHERE title = 'chocolate'

The values yielded by this query could be stored in a simple custom MinMaxPriceFilter value object that can be rendered easily in the JSP or Velocity template using standard JavaBeans display code. The values for the maximum and minimum price controls need only be loaded from the database if they are not already provided by the GridViewRequest object, as discussed below.

Implementing sorting: Headers or drop-down?

Sorting is another essential tool that allows users to manipulate the search results. Unlike filtering, sorting is a term that most users understand intuitively and can be simply labeled as Sort by:________, as Amazon.com does (Figure 3). Just as we did earlier with the filtering controls, we made the entire sorting control (including the caption) read like a natural English sentence.

Figure 3. Amazon.com’s sorting implementation using the “Sort by:” caption

To come up with a good sorting scheme, focus on the particular persona’s goals (that do not change) instead of on tasks (that change as technology and design evolve). For example, in the chocolate-cookbook use scenario, I set a goal to find the best quality cookbook in the 5 to 0 dollar range. My tasks were to filter based on price and category, and to sort in the “most highly rated items first” direction. I did not need the tool that sorted items based on category, publishing date, author’s last name, or the number of recipes. Adding those options would create more cognitive noise, clutter up the UI, and make it hard to find the functionality I actually needed to accomplish my goal.

Sorting can be implemented in the UI using clickable table headers (like those on Appartments.com) or using the HTML drop-down control (like those on Amazon.com). Clickable table headers save screen real estate and look slick. However, most large e-commerce sites implement result sorting as a drop-down control for several good reasons: First, not all the attributes on which personas would like to sort by are feasible to put in a grid. Second, the sort drop-down is more flexible and completely independent from the format and nature of the data in the grid. Third, and most importantly, in this implementation, we can specify precisely what items will appear first using natural language that maps best to a user’s mental model. For example, the “Price: Cheapest First” sort is clear, unambiguous, and goal-oriented.

In the drop-down sort control, avoid industry terms such as descending or ascending—most users find them confusing. Use sorting terms appropriate to the attribute being sorted: “Publication Date: Newest First” is a good way to describe the SQL query sort ORDER BY publish_date DESC.

A site with good usability should have a valid default sort order (which loads before the user has a chance to change anything) suitable for goals of a primary persona. The code below illustrates how to create a dynamic drop-down sort control. The default sort order is “Price: Cheapest First”:

 ArrayList sortOrder = new ArrayList();
sortOrder.add(new HtmlOption("Price: Cheapest First", "0");
sortOrder.add(new HtmlOption("Price: Highest First", "1");
sortOrder.add(new HtmlOption("Publication Date: Newest First", "2");

The drop-down control can be rendered in the Apache Velocity template using the renderHtmlSelectOptions Velocity macro introduced previously:

 <select name=" sortorder" size="1">
  #renderHtmlSelectOptions($sortOrder, $gridViewRequest.SortOrderId)

At runtime, the call to the macro generates the following HTML:

 <select name="sortorder" size="1">
  <option value="0" selected>Price: Cheapest First</option>
  <option value="1">Price: Highest First</option>
  <option value="2">Publication Date: Newest First</option>

It is easy to go overboard with adding sorting and filtering options to the UI when confusion pervades the persona’s goals and how specific sorting and filtering options help her accomplish those goals. Once again, picking the right primary persona to design for and running frequent usability tests will ensure that you add only the appropriate filtering and sorting controls, and avoid visual noise. Remember, the entire sorting control, including the caption, should read like a natural sentence.

Capturing filtering and sorting values using GridViewRequest

One way to simplify capturing users’ selections of sorting and filtering UI objects is to use the GridViewRequest object introduced in the code samples above. This value object (VO) interacts with the preloaded filter objects in the Velocity template to generate the dynamic values of all the filters and sorting selections in the user interface. GridViewRequest provides a convenient bridge between HttpServletRequest and business objects code and uses good object-oriented design practices such as achieving object immutability by using a static factory method that calls a private constructor (see Effective Java):

 //Value objects must be serializable for use in distributed Java systems like J2EE.
public class GridViewRequest implements java.io.Serializable {

  //Private attributes.
  private String parameter; //Simplified for clarity: use tokenize. implementation

  private int categoryId; //Store as int, but provide 2 accessors: as int and as String.
  private int sortOrderId; //Store as int, but provide 2 accessors: as int and as String.
  private float minPrice;
  private float maxPrice;

  //Private constructor ensures immutability.
  private GridViewRequest() {

  //This static factory method replaces the constructor.
  //It creates the VO directly from the HttpServletRequest object.
  //We need to collect the incoming values for:
  //1) search parameter (tokenized)
  //2) category
  //3) min price
  //4) max price
  //5) sort direction.
  //All these user inputs need to be cleaned and parsed.
  //Default is 0.0 for prices and 0 for categoryId and sortOrderId.
  public static GridViewRequest getInstance(HttpServletRequest request) {
    GridViewRequest gridViewRequest = new GridViewRequest();

    //Note: tokenize the user's search parameters as needed using a good
    //Java.util.Tokenizer implementation or similar --
    //see the Resources section for examples.
    listViewRequest.parameter = cleanString(request.getParameter("param"));

    //Min Price Filter:
    try {
      //If empty, min price is 0.0.
      if(isEmpty(request.getParameter("minprice"))) {
        listViewRequest.minPrice = 0.0;
      } else {
        //Try to parse the min price.
        listViewRequest.minPrice = Float.parseFloat(cleanString(request.getParameter("minprice")));
    } catch (Exception e) {
      //Exception resets min price back to 0.0.
      listViewRequest.minPrice = 0.0;

    //Parse and set maxPrice in a similar manner
    //(omitted for clarity).

    //Parse and set sortOrderId.
    //Default is 0 (i.e. sort order = "Cheapest first").

    try {

      //If empty, sortOrderId is 0.
      if(isEmpty(request.getParameter("sortorderid"))) {
        listViewRequest.sortOrderId = 0;
      } else {

        //Try to parse the sortOrderId.
        listViewRequest.sortOrderId = Integer.parseInt(cleanString(request.getParameter("sortorderid")));
    } catch (Exception e) {
      //Exception resets sortOrderId back to 0.
      listViewRequest.sortOrderId = 0;

  //Parse and set categoryId in the similar manner
  //(omitted for clarity).

  //Provide only accessor methods for all attributes to keep the object immutable.

  //Velocity calls toString() method on the attribute.
  //This method returns String to support Apache Velocity's attribute quick-access scheme.
  public String getCategoryId() {
    return Integer.toString(categoryId);

  //Velocity calls toString() method on the attribute.
  //This method returns String to support Apache Velocity's attribute quick-access scheme.
  public String getSortOrderId() {
    return Integer.toString(sortOrderId);

  //Add similar getter methods for parameter, minPrice, maxPrice, etc.,
  //utility methods.
  //A quick static helper method to cleanup the string.
  public static String cleanString(String dirty) {
    if(null == dirty)
      return "";

    //You may also want to remove or encode
    //any unsafe string attributes(', ", >,<, &, etc.)
    //in this method (omitted to safe space).

    return dirty.trim();

  //A quick static helper method determine if the string is empty or null.
  public static boolean isEmpty(String dirty) {
    if(null == dirty || dirty.length() < 1)
      return true;

    return false;

} //End of GridViewRequest class.

The object of the GridViewRequest class renders itself completely and cleanly, directly from the HttpServletRequest object in one simple call to the static factory method and remains immutable for its entire lifetime. Once formed, it can be passed to the method that constructs the SQL for the item search, where the user’s chosen values for the filter and sort controls can be applied dynamically to the search-results query:

 ...findItemWithFilter(GridViewRequest vo) {

  //Make appropriate adjustments for tokenized parameter.
  String sql = "SELECT * FROM item  WHERE title = '" + vo.getParameter() + "' ";

  //Apply category filter if supplied.
  if(! vo.getCategoryId().equals("0")) { //or use != 0 for the vo accessor method that returns int
    sql += " AND category_id = " + vo.getCategoryId() + " ";

  //Apply min price filter if supplied.
  //getMinPrice() returns a float, it must be formatted appropriately for use in SQL.
  if(vo.getMinPrice() > 0.0) {
    //Using > and subtracting 1 cent from MinPrice is a quick method to defeat Java float's formatting inconsistencies.
    sql += " AND price > " + vo.getMinPrice() - 0.01 + " ";

  //Use analogous method for MaxPrice (only make sure to add 1 cent instead of subtracting it)
  //(omitted for clarity).

  //Apply the sort order using Java case statement.
  //Make sure to use vo accessor method that returns an int.
  switch(gridViewRequest.getSortOrderIdAsInt()) {
    case GridViewRequest.NEWEST_FIRST: //2
    case GridViewRequest.MOST_EXPENSIVE_FIRST: //1
      sql += "ORDER BY PRICE DESC";

    //(the rest of sorting options are omitted to save space)

    default: //default sort order, case GridViewRequest.CHEAPEST_FIRST or 0
      sql += "ORDER BY PRICE ASC";

//Run the SQL normally to get the items

This method would be more efficient if I had used a StringBuffer to construct the SQL query string, but I used the overloaded + operator for clarity. It goes without saying that your finished code should have search parameter parsing, and all of the error handling and data cleanup of the incoming parameters associated with a Web application.

Traditional HTML controls are not the only way to implement filtering functionality. As I explain in the next section, to create a truly world-class experience for your users, sometimes it pays to look outside the traditional Internet browser box.

World-class filtering using category links

As I already mentioned, the concept of filtering is foreign to most users. Provided that sufficient amount of screen real estate is available, a truly world-class alternative to a drop-down filtering control is to use category links, as shown in Figure 4.

Figure 4. Category links and price extraction provide a natural and obvious filtering alternative. Click on thumbnail to view full-sized image.

Following one of the links will have the same effect as selecting one of the categories from a drop-down control and pressing the Go button in the previous desgin. However, links have many advantages over the drop-down control:

  1. They are highly visible and intuitive to use

  2. They are the original and universally accepted form of Web navigation

  3. They don’t require an additional click to see or to select from a drop-down control

  4. Users inherently feel safe selecting links, because if things go wrong, users feel they can just click the Back button

  5. Most importantly, well-designed links have the advantage of having a powerful information scent: that intuitive gut feeling of following the right path in the clutter of the UI overhead, ads, and pop-ups

For best results, category links should appear near the search box, not in the left- or right-side panel. In Figure 4, the search box and the link filters are aggregated in close spatial proximity, pointing to their close functional relationship. In addition, both controls are surrounded by a box to further reinforce the impression that they are indeed related controls. To reduce visual clutter that results from multiple links, show only the top four to five relevant categories and provide a link to “more…” that will show the remaining categories relevant to the user’s search results. The ellipsis (…) is a standard way to show that “more” is not in itself a category, but instead provides a link to additional information.

Note that the number of items listed after the links is approximate by design and will make an excellent homework assignment for the willing. This approximation is an attempt to make the item number appear more “human.” If you ask a fellow human being how many items were returned by the original chocolate-cookbook query, he will not, as the computer did, tell you “14,597 items,” because he understands that you do not really care about having full precision for such a meaningless number. Instead, he will tell you “about 14 thousand items” in the same way he would say “about eight o’clock” without giving you precise milliseconds of the Java timestamp. This approximation in typical human communication saves cognitive load and provides the receiving party with only the most important meaning of the information, without the cognitive clutter that the computer so “helpfully” delivers. Human beings are inherently less precise then computers in their communication, and the closer we can come to “humanizing” our user interface, the more intuitive, polite, and user-friendly we will make the system for our users. For more discussion about using approximate values to make the software more “polite,” see Cooper’s The Inmates are Running the Asylum.

Providing only the most relevant links as clickable category filters seamlessly combines the best features of both browsing and searching to create an optimal system interaction experience for users. Once the user clicks on the main category link, this interaction method naturally lends itself well to drilling down further into the relevant subcategories of the main “Cooking” category.

World-class filtering using price extraction control

Another useful and often overlooked filtering method is a natural language extraction control, also pictured in Figure 4. While the “made up” filters, such as product category, do not lend themselves well to being parsed using this method, many real-world concepts, such as a phone number, have a fairly predictable narrow range of user-entered formats that can be parsed with only a small amount of effort.

In our particular case, we can create an extraction control that naturally takes the place of the dedicated price filter in Figure 2. As this functionality is currently still quite rare, we should provide an example or simple instructions to show the users that this advanced functionality is available at our site. With some work, the system can support a variety of real-world pricing formats, such as “5-0” (pictured in Figure 4) as well as “5 to 0,” “around 5,” “less then 0,” etc. This functionality must implement a robust error-handling facility. Even if the program cannot completely parse an inherently esoteric and imprecise human request, it should be robust enough to make an educated guess that users can correct as appropriate (think Google’s self-correcting search errors). Given that it is relatively easy to create extraction controls, it is astonishing how few companies currently implement this (Google Local is one). I hope this article will help remedy this situation in some small way.

World-class filtering using a dual slider

To create world-class system interaction, it is important to come up with a UI control that offers both strong clues to its operation and the appropriate mapping to the system property being controlled. According to the classic usability book The Design of Everyday Things, “…the term affordance refers to the perceived and actual properties of the thing, primarily those fundamental properties that determine just how the thing could possibly be used… Plates are for pushing. Knobs for turning. Slots are for inserting things into.”

Consider that users have no restrictions on what they can type into the minimum and maximum price text boxes suggested above, including nonsense like “-,000,000” and “AbC.” Let’s extend the quote above: “The text box is for typing things into.” This statement does not necessarily suggest or constrain what can be typed. An alternative control with rich visual feedback and correct affordance for the price filtering task is a dual slider pictured in Figure 5.

Figure 5. Dual slider control with correct affordance for specifying price ranges.

The control pictured in Figure 5 is actually Amazon.com’s dual slider control used in the diamond search page. I modified it slightly so that it always shows the minimum and maximum prices of the price range for relevant items. Another great thing about this control is the item count that is loaded dynamically as the user slides the control to show how many items are now found in constrained search results (see Resources for more information).

Regardless of the actual UI implementation, the output of the dual slider will be the user-defined positions of the minimum and maximum pegs that can be easily plugged into the backend code we discussed above. Dual sliders are wonderfully intuitive for setting the active minimum and maximum end-points for a range of values because they look and feel so much like real-world controls. Unfortunately, sliders of any kind are not yet a part of the standard HTML toolkit and are extremely rare. (See Resources for some Ajax slider implementations.) At the time of this writing, even Amazon.com is using the slider only for the diamond search page and not for the common items’ search pages. Perhaps some day soon these types of visual filtering controls with rich, modeless feedback will become more widespread to help us accomplish our online computing goals faster and easier.


Usability and interaction design is the new Internet frontier. The increasing popularity of easy-to-use sites like Google with the appropriate toolkits of “just the right kind” of simple and powerful controls shows that focusing on user experience is the key to long-term success and incredible customer loyalty. No amount of cool technology will rescue the product if the UI is hard to use, cluttered, or poorly designed. As an engineer, if you are not asking your team, “Can Alan, my primary usability persona, accomplish his goal?” and “How is this function helping Alan kick butt?”, you are just wasting precious time creating useless features that only clutter the interface and confuse the users.

As my Amazon experience shows, the search-results page is a great place to start improving your system’s usability. Even on some of the leading e-commerce sites, this important page is frequently made hard to use by visual clutter or absence of appropriate sorting and filtering tools. In this article, I presented some ideas for improving the search-results page and provided some examples of world-class search filter design. I also tried to show that good interaction design of sorting and filtering functionality does not happen in a vacuum, but is instead based solidly on the goals and behaviors of real users modeled as appropriate user personas. Successful design also involves testing the usability of prototypes early and often, and watching carefully as real customers struggle with your cool new system.

Giving your users sophisticated controls involves treating them with respect, as alert and caring (though frequently distracted and impatient) partners in the human-computer interaction. In the end, the design goal to strive for is an interface so simple, elegant, and straightforward that it simply disappears, allowing the user to arrive at his or her goal “magically.” Granted, many companies currently have a strict policy of not presenting software prototypes or unwashed engineers to their users, but this approach will have to change fast. Otherwise, a user who finds sweat socks when looking for chocolate will start buying cookbooks somewhere else.

Join the conversation

or to participate.