In a simple search, customers search the catalog by entering text. For example, a customer could search for “shatter-proof helmets.” The customer’s input is searched as a complete string (for example, the above query would be based on “shatter-proof helmets” and not “shatter-proof” or “helmets”). We used SearchFormHandler
to build up the query to apply to the SQL Repository (Product Catalog) and to return a result set (empty if no products are found). The result set(s) 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 handler.
We implemented the simple search feature as a collection of Java Server Pages, page fragments, and a Nucleus component configuration file (properties file). The configuration file creates and configures a Nucleus component of type 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
. Keywords are a one-to-many property and the others are one-to-one properties. A keyword search looks through all keyword properties for a string input. A text search looks through all text properties for a string input, but does not search keyword properties.
SimpleSearchFragment.jsp
We wanted customers to be able to search from other store pages besides the search page, SimpleSearch.jsp
. Therefore, we created the small search form fragment that defines the search text entry field and the Search button to embed in any page that requires searching.
This fragment imports the CatalogSearch
component in order to configure its searchInput
property and invoke its seach
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 imbedded on any page that requires a search capability. The FormAction parameter is the name of the page to redirect to display the results of the search. --> <DECLAREPARAM NAME="FormAction" CLASS="java.lang.String" DESCRIPTION="The name of the that will be the action of this form"> <dsp:importbean bean="/atg/userprofiling/Profile"/> <dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/> <dsp:importbean bean="/atg/commerce/catalog/CatalogNavHistory"/> <!-- Title: Simple Search Form Fragment --> <dsp:getvalueof id="successUrl" param="FormAction" idtype="java.lang.String"> <dsp:form action="<%=successUrl%>" method="POST"> Search for <dsp:input bean="CatalogSearch.searchInput" size="20" type="text"/> <input name="repositoryKey" type="hidden" value='<dsp:valueof bean="/OriginatingRequest.requestLocale.locale"/>'> <!-- 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"/> </dsp:form></dsp:getvalueof>
The hidden input tag is an HTML (rather than ATG-specific) 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.
SimpleSearch.jsp
SimpleSearch.jsp
is a page built of several JSP code fragments that provide their own specific functionality. The SimpleSearch
page uses two search-specific page fragments: SimpleSearchFragment.jsp
accepts the search input string and invokes the search itself and SearchResultsFragment.jsp
displays the search results.
SimpleSearchFragment.jsp
takes a parameter called FormAction
to indicate the page to go to when the form is submitted. In SimpleSearch.jsp
, this value is set to SimpleSearch.jsp
, causing the search page to reload when the search completes.
SearchResultsFragment.jsp
is passed the value of the CatalogSearch
component’s searchResultsByItemType
parameter in order to format the results.
The following example shows SimpleSearch.jsp
:
<% /* This JSP code demonstrates how to perform a simple search on the * product catalog repository. The CatalogSearch form handler component takes customer input and returns search results. This form handler is configured with the following prooperties: * * doKeywordSearch=true # search for input in keyword properties * keywordPropertyNames=keywords # which properties to search * itemTypes=category,product # which repository item types to search * $scope=session # the CatalogSearch component is session scoped * * The CatalogSearch form handler's handleSubmit() method will * generate a repository query based on the input. The results of * the query are retrieved via the * CatalogSearch.searchResultsByItemType property and passed to the * SearchResultsFragment JSP droplet to be displayed. */ %> <dsp:importbean bean="/atg/dynamo/droplet/ForEach"/> <dsp:importbean bean="/atg/commerce/catalog/CatalogSearch"/> <dsp:importbean bean="/atg/commerce/catalog/Navigator"/> <dsp:setvalue bean="/atg/userprofiling/Profile.currentLocation" value="catalog_search"/> <dsp:include page="../common/HeadBody.jsp" flush="true"></dsp:include> <dsp:include page="../common/StoreBrand.jsp" flush="true"></dsp:include> <span class=storelittle> <% /* Display Navigation information (list of all parent * categories as links users can jump to) */ %> <dsp:include page="../catalog/common/breadcrumbs.jsp" flush="true"> <dsp:param name="displaybreadcrumbs" value="true"/> <dsp:param name="no_new_crumb" value="true"/> </dsp:include> </span> <p> <span class=storebig>Shop by Searching</span><br> <p> <table cellpadding=0 cellspacing=2 border=0> <tr> <td class=box-top-store>Simple Search</td> </tr> <tr> <td class=box> <% /* Use the SimpleSearchFragment JSP droplet, which contains * a simple form, to get the search input and to invoke the * CatalogSearch component's handleSubmit() method. */ %> <dsp:include page="SimpleSearchFragment.jsp" flush="true"><dsp:param name="FormAction" value="SimpleSearch.jsp"/></dsp:include> <p> <dsp:a href="AdvancedSearch.jsp">Use the advanced search form instead</dsp:a> <br><font size=-1><i>Simple search result set size limited to 50.</i></font><br> </td> </tr> </table> <p> <%/* Display the results with the SearchResultsFragment JSP droplet */%> <dsp:getvalueof id="pval0" bean="CatalogSearch.searchResultsByItemType"> <dsp:include page="SearchResultsFragment.jsp" flush="true"> <dsp:param name="ResultArray" value="<%=pval0%>"/> </dsp:include></dsp:getvalueof> <p> <dsp:include page="../common/CatalogFooter.jsp" flush="true"></dsp:include> <dsp:include page="../common/Copyright.jsp" flush="true"></dsp:include> </BODY> </HTML>
SearchResultsFragment.jsp
We used the SearchResultsFragment
page fragment to iterate and display search results from both SimpleSearch.jsp
and AdvancedSearch.jsp
. ForEach
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 both SimpleSearch
and AdvancedSearch
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
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 OPARAM
tag of the Switch
is rendered. In this case, another call to the ForEach
component iterates through the array and ItemLink.jsp
is invoked for each element to properly format and display the repository item.
<% /* --------------------------------------------------------------- This jsp droplet demonstrates how to display 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/ForEach"/> <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"> <b>We found these categories matching your search</b> <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="elementName" value="Category"/> <dsp:oparam name="output"> <%-- Display a link to the Category: --%> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Category"/> <dsp:param name="Image" param="Category.thumbnailImage"/> <dsp:param name="DisplayText" param="Category.displayName"/> <dsp:param name="navAction" value="jump"/> </dsp:include> <br> </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"> <p> <b>We found these products matching your search</b> <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="elementName" value="Product"/> <dsp:oparam name="output"> <dsp:valueof param="element.manufacturer.displayName"/> <%/* Display a link to the Product: */%> <dsp:include page="../common/ItemLink.jsp" flush="true"> <dsp:param name="Item" param="Product"/> <dsp:param name="Image" param="Product.thumbnailImage"/> <dsp:param name="DisplayText" param="Product.displayName"/> <dsp:param name="navAction" value="jump"/> </dsp:include> <br> </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>