Overview
When a user performs a search using a find form, add the ability to save the search criteria and then on subsequent visits to the form, make available the list of saved searches for the user to select from (in addition to the regular fields). This will improve the user workflow by
providing easy access to frequently used searches. This proposal is only concerned with long term persisted searches, it is not intend to handle recent view histories nor is it concerned with saving searches for only the duration of the user session (although this implementation could assist the implementation of those features). The functionality is similar to Jira's filters and SugarCRM's Saved Searches (obviously not the implementation just the functionality).
Previous discussions related to this topic can be found here:
- 2008-12-08 - http://n4.nabble.com/Save-Search-in-database-td196166.html#a196167
- 2009-10-26 - http://n4.nabble.com/Searches-done-by-default-td213375.html#a221317
User Interaction
- When the user visits a find screen, if they have previously saved any searches then a drop-down input will be present in addition to the regular find form fields.
- If the user chooses not to use any of the saved searches but instead opts to use the find form as normal, then upon submission the find form will also present an option to save the search. This will be presented as a text input for the name of the search and a submit button. The save search option should only be presented when the user has submitted the form as normal and will not be present when the user has chosen a previous search from the drop-down.
- If the user selects a search from the drop-down instead of using the find form then the saved search will be performed and the find form will also be populated with the saved parameters
- If the user modifies a saved search by altering the form fields after performing the operation above then the user will be presented with the option of either saving the search as a new one or updating the existing saved search with the new parameters.
Implementation
Data Model
Two new entities will be required:
PersistedForm - Main entity
- persistedFormId - Sequenced primary key
- formTargetUrl - Stores the target request (would be the target from the find form), this would be used to find persisted searches for populating the drop-down box and also as the target for persisted search submission
- name - The user provided name for the saved search
- persistanceType - This will mostly be a place holder for now, refer to potential uses below
- userLoginId - The user for whom the search has been persisted, allowed to be null in the case of global saved searches i.e. available to anyone who would normally be able to view the formTargetUrl
- createdByUserLoginId - means to keep track of who created a global saved search
- createdDate - Timestamp for when the search was persisted
- hitCount - Keeps track of number of times the persisted search has been used, could allow for floating the most commonly used searches to the top of the drop-down (not sure about this, just a thought)
PersistedFormParam - Child entity containing the request parameters for the request
- persistedFormId - Primary and also Foreign Key to PersistedRequest
- parameterName - The name of the parameter and also part of the Primary Key
- parameterValue - The value of the named parameter
I'm not sure about the names but given the #Other potential uses of the entities, I've tried come up with something as generic as possible, the data could also be considered to represent a persisted request rather than a form. Other suggestions are welcome
Form Widget
In order to enable a saved search within a form widget, I think the best approach would be to provide a new field type specifically for this purpose. Doing so will allow the developer to position the save search form elements within the form as desired and also allow the elements to be styled.
Proposed syntax:
<form> <persisted-form> <load-form-drop-down title=""tooltip="" title-area-style="" widget-area-style="" title-style="" widget-style="" tool-tip-style="" id-name=""/> <save-form-text size="" max-length title=""tooltip="" title-area-style="" widget-area-style="" title-style="" widget-style="" tool-tip-style="" id-name=""/> </persisted-form> </form>
- The widget field will only be applicable for type="single" forms
- The persisted-form element will be a child of the form element (i.e. same level as the field element) and will use a subset of the field element attributes, i.e. the ones that make sense in this context for example the name attribute won't be present because the input names will be hard-coded.
- Only one persisted-form element will be allowed.
- Both load-form-drop-down and save-form-text will be optional children of persisted-form, to be used only when you need to override the system defaults. So to add the feature to a form will generally be as simple as adding a <persisted-form/> tag.
- The persisted-form will perform in the following ways depending state of the _PERSISTED_FORM_ID_ parameter in the context:
- _PERSISTED_FORM_ID_ is missing from the context - this means the form has yet to be submitted so the drop down list of persisted searches should be presented to the user. The list is retrieved by performing a lookup on the userLoginId and the form's target url.
- _PERSISTED_FORM_ID_ is present in the context, but empty - The user has performed a search by the regular means and we should render the text input and submit button to allow them to name and save the search. In this instance there will be no hidden form but the on-submit event will alter the form's target to point to the common-controllers.xml saveForm request, the removed target will be inserted into the form as a hidden parameter and the entire form submitted to be saved. Using the form in situ (rather than an alternate hidden form) will allow the user to make adjustments to the search criteria before saving.
- _PERSISTED_FORM_ID_+ is present from the context in the context and not empty - A saved search was requested so we should display the drop-down list with the current search selected. If for some reason the field contains an invalid value then we should treat it as empty and render the text box as above. The incoming context should also be checked against the persisted parameters to determine if the saved search has been modified and if so then provide options to either save the search as a new one (via a text-box and submit as above) or to update the saved search with the new parameters (drop-down with update link next to it).
We should be able to support the use of this feature within FreeMarker templates using a transform macro without too much trouble.
Saving the search
common-controller saveForm request, runs a service event that persists the search and then performs a request-redirect to the loadForm request (see below) with the newly created _PERSISTED_FORM_ID_ as a url parameter. If a _PERSISTED_FORM_ID_ is present in the incoming saveSearch parameters then the saved search represented by that id should be updated rather than creating a new one.
Loading the search
common-controller loadForm request, runs a java event that retrieves the form parameters based on _PERSISTED_FORM_ID_ and inserts them into the request attributes, then manually performs a "request" type response (using requestHandler.doRequest(...)) that targets the persistedForm.formTargetUrl. Basically performs a direct simulation of the persisted request except that the persisted parameters end up as request attributes rather than parameters.
Other potential uses
- List iteration - not sure if it would provide any benefits over the current iteration mechanism
- Record level iteration - i.e. search for a product, select one and view it but have iteration available within the product view. As for list iteration, record iteration is possible without this feature but it may make life easier if we don't have to maintain all the url parameters.
- Save For Later functionality (persist a partially completed form/draft), would need to add an attribute to the persist-form element to allow a different target to be specified e.g. when creating a product, you wouldn't want the target to be createProduct (which would persist the product) but instead EditProduct so the user can continue filling out the form.
- Could potentially be useful for allowing custom rss or other data feeds in a generic manner.
1 Comment
Jacques Le Roux
Thanks Scott,
Well done! Just a question, why manually in <<then manually performs a "request" type response>> ? (maybe it's my limited understanding of English language which makes me asking this question...)