In a simple search, customers search the catalog by entering text. For example, a customer could search for “air filter.” The customer input is searched as a complete string (for example, the above query would be based on “air filter” and not “air” or “filter”). We used the SearchFormHandler
to create the query to the SQL Repository (Product Catalog) and to return a result set. It returns an empty set if no products are found. The result set is a collection of repository items that can be displayed in any format. Simple searches return both products and categories.
We used properties or configuration files to define the action a page performs and Boolean arguments to specify the type of searching to perform. Property names specify which catalog properties to search. The single configurable form handler simplifies catalog searching and should support all searching requirements. If your store requires custom searching beyond configuration changes, you can easily extend this form handler or write another one.
We implemented the search feature in Motorprise with a combination of JSPs, JSP fragments, and a Nucleus component configuration file (properties file). The properties file creates and configures a Nucleus component of class atg.commerce.catalog.SearchFormHandler
to perform the search. The simple search functionality allows the user to enter a string and get products or categories that match the keywords as results.
Products have multiple properties such as keywords
, description
, longDescription
, and displayName
. The keywords
property is a multi-valued property and the others are single-valued properties. Thus, for every product, there can be multiple keywords associated with it. A keyword search looks through all keyword properties for a string input. A text search looks through description
and displayName
for a string input, but does not search keyword properties. By default, a keyword and text search is done on each query. This means that both keywords as well as description and display name will be queried.
SimpleSearchFragment.jsp
We wanted customers to be able to search from other store pages besides the search page, simple_search.jsp
. Therefore, we created this small search form fragment, /en/search/SimpleSearchFragment.jsp
, that defines the search text entry field and the Search button to embed in any page that requires searching. We embedded this fragment in the Motorprise header so that users can search from every page.
This fragment imports the CatalogSearch
component in order to configure its searchInput
property and invoke its search handler.
Finally, the form redirects to the page name passed via the FormAction
parameter. This page fragment declares a page parameter that is used as the action for the search form. In other words, the FormAction
parameter is the file name of the page to which the user is redirected when the search form’s submit
method is invoked.
The following example shows SimpleSearchFragment.jsp
:
<%/* This page fragment is intended to be embedded on any page that requires a search capability. */%> <dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/> <dsp:form action="simple_search.jsp" method="POST"> <table cellspacing="0" cellpadding="0" border="0" bgcolor="#999999" width=100%> <tr><td colspan=3><dsp:img src="../images/d.gif" vspace="0"/></td></td></tr> <tr> <td><dsp:img src="../images/d.gif" hspace="3"/></td> <td width=100> <!-- form elements --> <input name="repositoryKey" type="hidden" value="<dsp:valueof bean='/OriginatingRequest.requestLocale.locale'/>"> <dsp:input bean="CatalogSearch.searchInput" size="15" type="text" value=""/> </td> <!-- use this hidden form tag to make sure the search handler is invoked if someone does not hit the submit button --> <td align=center> <dsp:input bean="CatalogSearch.search" type="hidden" value="Search"/> <dsp:input type="image" src="../images/motorprise-search.gif" bean="CatalogSearch.search" name="search" value="Search" border="0"/> </td> <!-- end form elements --> </tr> <tr> <td><dsp:img src="../images/d.gif" hspace="3"/></td> <td colspan=2><dsp:a href="advanced_search.jsp"><font color="#FFFFFF"><span class=smallw>Advanced search</span></font></dsp:a></td> </tr> <tr><td colspan=3><dsp:img src="../images/d.gif" vspace="0"/></td></tr> </dsp:form> </table>
The hidden input tag is an HTML workaround for forms with a single input field. The hidden input allows the user to invoke the submit
method by typing the return key within the text input field.
Without this hidden tag, the user must click the Search button.
simple_search.jsp
The simple_search.jsp
is a page built of several JSP code fragments that provide their own specific functionality. simple_search.jsp
uses SearchResults.jsp
to display the results of the search.
First, we display the search results at the top of the page by including SearchResults.jsp
. The CatalogSearch
component is used to perform the actual searching. When the user submits the search, the handler search method is invoked. This basically fills up the search results by ItemType
property of catalogSearch
.
The following example shows simple_search.jsp
:
<%@ taglib uri="dsp" prefix="dsp" %> <dsp:page> <dsp:importbean bean="/atg/dynamo/droplet/ForEach"/> <dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/> <dsp:importbean bean="/atg/dynamo/droplet/Switch"/> <DECLAREPARAM NAME="noCrumbs" CLASS="java.lang.String" DESCRIPTION="This is for deciding what kind of breadcrumbs to display. If this is true, then breadcrumbs will show: Store Home|Search, instead of nav history. Default is false." OPTIONAL> <dsp:include page="../common/HeadBody.jsp" flush="true"><dsp:param name="pagetitle" value=" Simple Search"/></dsp:include> <table border=0 cellpadding=0 cellspacing=0 width=800> <tr> <td colspan=2><dsp:include page="../common/BrandNav.jsp" flush="true"></dsp:include></td> </tr> <tr bgcolor="#DBDBDB" > <td colspan=2 height=18> <!-- breadcrumbs --> <span class="small"> <dsp:droplet name="Switch"> <dsp:param name="value" param="noCrumbs"/> <dsp:oparam name="true"><dsp:a href="../home.jsp">Product Catalog</dsp:a> Simple Search</dsp:oparam> <dsp:oparam name="default"><dsp:param name="noCrumbs" value="false"/> <dsp:include page="../common/breadcrumbs.jsp" flush="true"><dsp:param name="displaybreadcrumbs" value="true"/><dsp:param name="no_new_crumb" value="true"/></dsp:include> </dsp:oparam> </dsp:droplet></span> </td> </tr> <tr> <td width=55><dsp:img src="../images/d.gif" hspace="27"/></td> <td valign="top" width=745><br><span class="big">Search</span></td> </tr> <tr> <td width="40"><dsp:img src="../images/d.gif"/></td> <td><br> <dsp:getvalueof id="pval0" bean="CatalogSearch.searchResultsByItemType"> <dsp:include page="SearchResults.jsp" flush="true"><dsp:param name="ResultArray" value="<%=pval0%>"/></dsp:include></dsp:getvalueof> </td> </tr> <tr><td colspan=2><dsp:img src="../images/d.gif" vspace="6"/></td></tr> <tr> <td width="40"><dsp:img src="../images/d.gif"/></td> <td><!-- simple search box --> <table bgcolor="#FFCC66" border=0 cellpadding=0 cellspacing=0> <tr> <td colspan=3> <table width=100% cellpadding=4 cellspacing=0 border=0> <tr><td class=box-top> Simple Search</td></tr> </table> </td> </tr> <tr><td bgcolor="#666666"><dsp:img src="../images/d.gif" width="1"/></td> <td> <dsp:form action="simple_search.jsp" method="POST"> <table width=100% cellpadding=6 cellspacing=0 border=0> <tr> <td></td> <td bgcolor="#ffcc66"> <input name="repositoryKey" type="hidden" value="<dsp:valueof bean="/OriginatingRequest.requestLocale.locale"/>"> <dsp:input bean="CatalogSearch.searchInput" size="30" type="text"/> <input name="noCrumbs" type="hidden" value="<dsp:valueof param="noCrumbs"/>"> <!-- use this hidden form tag to make sure the search handler is invoked if someone does not hit the submit button --> <dsp:input bean="CatalogSearch.search" type="hidden" value="Search"/> <dsp:input bean="CatalogSearch.search" type="submit" value="Search"/><br><!--<span class="help">Separate words or phrases by <b>AND</b> or <b>OR</b></span>--> <p> <span class=smallb><dsp:a href="advanced_search.jsp"> <dsp:param name="noCrumbs" param="noCrumbs"/> Use advanced search form</span></dsp:a> </td> </tr> <tr> <td></td> <td> <span class=smallb><dsp:a href="part_number_search.jsp"> <dsp:param name="noCrumbs" param="noCrumbs"/> Use part number search form</dsp:a></span> </td> </tr> </table> </dsp:form> </td> <td bgcolor="#666666"><dsp:img src="../images/d.gif" width="1"/></td> </tr> <tr><td bgcolor="#666666" colspan=3><dsp:img src= "../images/d.gif"/></td></tr> </table> </td> </tr> </table> </body> </html> </dsp:page>
SearchResults.jsp
We used the SearchResults
page fragment to iterate and display search results from simple_search.jsp
. The ForEach
component is sent the parameter array, which can take many kinds of multi-valued objects.
The type in this case is a java.util.Map
that is the product of the SearchFormHandler
component.
The map has one entry for each value specified in the itemTypes
value of the component’s configuration file. In SimpleSearch
, the itemTypes
are category
and product
.
The map is a collection of search result arrays and their associated itemType
identifiers. The value associated with category and product in the map is an array of category or product items, or null if no results are found for that key.
The category and product map elements are extracted by iterating through the map with the standard ForEach
component in the outermost ForEach
droplet tag. For each element of the map, the Switch
component is then invoked to distinguish the category and product elements, and to affect the order in which they’re rendered on the page.
Once the category and product search result array is obtained, it is necessary to iterate through each element of that array to display the results. In order to detect an empty result array, we used another Switch
component, this time for the unset dsp:oparam
feature of Switch
that identifies an empty input array and a message is rendered indicating that no items of the given type were found. When the array is non-null, the default dsp:oparam
tag of Switch
is rendered. In this case, another call to the ForEach
component iterates through the array and displays each of its items.
<%@ taglib uri="dsp" prefix="dsp" %> <dsp:page> <% /* --------------------------------------------------------------- This JSP droplet displays the contents of search that potentially returns both category and product repository items. The one paramater, ResultArray, accepts a HashMap that contains elements with the keys "category" and "product". The values of these keys are collections of category or product repository items found in the search. ----------------------------------------------------------------*/ %> <DECLAREPARAM NAME="ResultArray" CLASS="java.util.Map" DESCRIPTION="Array of Search Results"> <dsp:importbean bean="/atg/dynamo/droplet/Switch"/> <dsp:importbean bean="/atg/dynamo/droplet/IsEmpty"/> <dsp:importbean bean="/atg/dynamo/droplet/ForEach"/> <dsp:importbean bean="/atg/dynamo/droplet/RQLQueryForEach"/> <dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultArray"/> <%/*Each item in this array is a Collection of Categories or Products...*/%> <dsp:param name="elementName" value="ResultCollection"/> <dsp:oparam name="output"> <dsp:droplet name="Switch"> <%--The key tells us if this is a Collection of Products or Categories: --%> <dsp:param name="value" param="key"/> <%--For the list of CATEGORIES: --%> <dsp:oparam name="category"> <blockquote> <dsp:droplet name="Switch"> <dsp:param name="value" param="ResultCollection"/> <dsp:oparam name="default"> <p> <%-- For each Category in the Collection: --%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultCollection"/> <dsp:param name="sortProperties" value="+displayName"/> <dsp:param name="elementName" value="Category"/> <dsp:oparam name="outputStart"> <b>We found these categories matching your search</b> <p> </dsp:oparam> <dsp:oparam name="output"> <%-- Display a link to the Category: --%> <dsp:getvalueof id="urlStr" idtype="java.lang.String" param="Category.template.url"> <dsp:a page="<%=urlStr%>"> <dsp:param name="id" param="Category.repositoryId"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="Item" param="Category"/> <dsp:valueof param="Category.displayName">No name</dsp:valueof> </dsp:a> </dsp:getvalueof> <br> </dsp:oparam> <dsp:oparam name="empty"> <b>There are no categories matching your search</b> <p> </dsp:oparam> </dsp:droplet> </dsp:oparam> <%-- If NO Categories returned by the search: --%> <dsp:oparam name="unset"> No category items in the catalog could be found that match your query </dsp:oparam> </dsp:droplet><%/*ForEach Category*/%> </blockquote> <P> </dsp:oparam> <%-- For the list of PRODUCTS: --%> <dsp:oparam name="product"> <blockquote><p> <dsp:droplet name="Switch"> <dsp:param name="value" param="ResultCollection"/> <dsp:oparam name="default"> <%/*For each Product in the Collection: */%> <dsp:droplet name="ForEach"> <dsp:param name="array" param="ResultCollection"/> <dsp:param name="sortProperties" value="+displayName"/> <dsp:param name="elementName" value="Product"/> <dsp:oparam name="outputStart"> <p> <b>We found these products matching your search</b> <p> </dsp:oparam> <dsp:oparam name="output"> <%-- Display a link to the Product: --%> <dsp:getvalueof id="urlStr" idtype="java.lang.String" param= "Product.template.url"> <dsp:a page="<%=urlStr%>"> <dsp:param name="id" param="Product.repositoryId"/> <dsp:param name="navAction" value="jump"/> <dsp:param name="Item" param="Product"/> <dsp:valueof param="Product.displayName">No name </dsp:valueof> - <dsp:valueof param="Product.description"/> </dsp:a> </dsp:getvalueof> <br> </dsp:oparam> <dsp:oparam name="empty"> <b>There are no products matching your search</b> <p> </dsp:oparam> </dsp:droplet> <%/*ForEach Product*/%> </dsp:oparam> <%/*If NO Products returned by the search: */%> <dsp:oparam name="unset"> No product items in the catalog could be found that match your query<p> </dsp:oparam> </dsp:droplet> </blockquote><P> </dsp:oparam> </dsp:droplet> </dsp:oparam> </dsp:droplet> <%/*ForEach Item returned by Search */%> </dsp:page>