Filtering large sets of data is a common interaction in today's web applications. Examples include filtering web pages (e.g. Google), filtering e-mails (e.g. Gmail), filtering flights (e.g. Kayak) and filtering jobs (e.g. monster). The user is usually interested in a subset of the data, which can be specified by various types of constraints. The most common one is probably finding items matching a set of search terms. But other constraints are also found, such as constraining the result set by a category or a set of categories (e.g. jobs in IT) or by ranges (e.g. gifts with a price between $10 and $20, flights leaving SFO between 4pm and 6pm).
HTML provides various form elements used to build these interactions. The text input is probably the most used element, but check boxes, radio buttons and drop-down lists are also available. Interestingly, there is no built-in element to select ranges. The main reason is probably that there is no right way to represent a range. For instance, selecting a range of dates should be done with a calendar-like widget while selecting a price should be done with a simple slider. Furthermore, rendering an element to select ranges usually requires some knowledge about the data it selects (e.g. the bounds). In this project, we are building a histogram-based slider for selecting numeric ranges. We are especially interested in studying how the element can be packaged and reused in different applications. Is it possible to make it as easy to use as a built-in form element such as a text input?
Histogram-based sliders have an advantage over simple sliders. They provide the user some information about the data distribution as well the number of results he is filtering.
Client side Edit
Server side Edit
new_search_with_histogrammethod processes the POST and GET parameters and returns the filtered results. It is called in the controller, and the result is passed to the view.
histogrammethod calculates the bucket sizes by issuing
SELECT COUNT(*)...SQL statements for a given model and column. It is used by our helper method. The user doesn't have to know about this method, unless he has to compute the bucket sizes with a different method (e.g. without SQL).
Google Chart API Edit
The background image of the histogram is rendered using the Google Chart API. The advantages and disadvantages of this approach are discussed in the last section.
Encapsulation and reusability Edit
Benefits from the framework facilities Edit
- Rails has a plug-in architecture with a console script
- Ruby does not close classes. This means that we can add methods to a previously defined class by simply reopening the class. We make extensive use of this feature to add our code to preexisting classes. Data related methods (i.e. histogram generation and filtering) are added to the
ActionViewclass. The plug-in system provides a callback when the application is loading (i.e. init.rb is run at the appropriate time). This allows us to modify the
ActionViewclasses when the server is starting.
- Ruby's metaclass support (which allows methods to be added to particular instances of a class) is used to pass data without the explicit knowledge of the developer. In order to hide the internal details of our system, we do not want to force the developer to explicitly pass variables between various components (e.g. from the model to the view through the controller). This problem is made especially difficult given the shared-nothing architecture of Rails. For example, we must keep the histogram boundary locations in addition to the values at those locations in order to preserve the state of the control after the user executes a search. We do this by adding methods and variables to the instance of the Model returned from our
ActiveRecordmethods. As the developer passes the results to the view, our state information is similarly passed.
Assuming that the main Model we want to filter is called
1. Install the plug-in into your Rails application: Edit
script/plugin install http://rails-histogram.googlecode.com/svn/trunk/
2. Include the following in the desired method of the controller: Edit
@search = Student.new_search_with_histogram(params)
3. Include the following in view: Edit
<%= sparkline @search, :gpa, options... %>
|:buckets||int||10||Number of buckets for histogram|
|:width||int||400||Width of histogram image in px|
|:height||int||100||Height of histogram image in px|
|:space||int||0||Space between bars in the histogram image|
|:rescale||boolean||true||Use log scale for y-axis when true, linear axis otherwise|
|:submit||string||'search_submit'||id of submit button to use after histogram is manipulated|
These files are copied when the plug-in is installed.
- Our interaction is as decoupled from the form than a built-in element such as a text input. We don't need to use a special kind of form, nor do we have to do some complex wiring between the histogram and the form. The histogram can be added to any existing form without modifying the rest of the form. Furthermore, the histogram is completely decoupled from the way the form is submitted. If it is an AJAX form, then our interaction is asynchronous. If it is a traditional form, then our interaction is synchronous.
- The complex logic required to generate the histogram buckets is encapsulated in our plug-in and added to
ActiveRecordfor free. Even though 90% of our code is client side, the extra 10 percents make the integration of our component a lot easier for the developer.
- Histogram sliders are useful in a lot of applications.
- Instead of relying on the Google Chart API, we could provide some server side code generating the image. It would basically do exactly what the Google Chart API is doing, but would provide fine grained control on how the histogram is rendered. The disadvantage of this approach is that generating the image requires the usage of some complex image generation libraries such as ImageMagick.
- Instead of rendering an image, we could create the histogram using DIVs. This approach is very fast and probably quite easy to implement. Furthermore, it could be entirely configured using CSS, which is nice. However, if the number of buckets is high or if there are many histograms on the page, this approach could potentially use a lot of memory and slow down the browser.
- Histogram sliders are useful in a lot of applications, but not in all applications. This input method is more specialized than, say, a text box and has thus less use cases. What the outcome of this project is hopefully proving is that it is possible to create reusable form elements requiring some knowledge about the back-end data and, thus, spanning the server-client divide. The histogram-based slider is only an example of such reusable elements.