Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The item import may be found here:

Panel

Home Items Batch Item Import

By using the spreadsheet column headers he did, Jon easily imported the file because the import wizard was able to automatically match the attribute columns to the proper field names.   Sure beats manually assigning 20 columns.

...

The next step is to create the template file.

Navigate to:

Panel

Home Catalog Manage Catalog Templates

and create a new template file.

...

Here is the file (also attached: harness_search.vm)   It is heavily commented, so please read through it carefully.

Code Block
themeDJango
languagehtml/xml
themeDJango
titleAdvanced Item Search Catalog Screen
linenumberstrue
collapsetrue
## the following comments help smart editors display velocity syntax help.
## you don't have to have them in your file, but if UltraCart developers get involved, they'll add them.
## UltraCart uses Intellij.  Only the premium (pricey) version has velocity support, but it is *AMAZING*.
#* @vtlvariable name="formatHelper" type="com.bpsinfo.ultracart.catalog.tobjects.FormatHelper" *#
#* @vtlvariable name="baseUrl" type="java.lang.String" *#
#* @vtlvariable name="group" type="com.bpsinfo.ultracart.catalog.tobjects.GroupImpl" *#
#* @vtlvariable name="advancedItemSearchManager" type="com.bpsinfo.ultracart.catalog.tobjects.advitemsearch.AdvancedItemSearchManager" *#
#* @vtlvariable name="parameters" type="java.util.HashMap<java.lang.String,java.lang.String>" *#
#* @vtlvariable name="parameters2" type="java.util.HashMap<java.lang.String,java.util.List<java.lang.String>>" *#
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
## TODO - change this to your own title...
  <title>Harness Search - PSI</title>
  <style type='text/css'>
    body {
      font-family: verdana, serif;
      font-size: 0.8em;
    }
    .search_value {
      width: 180px;
      display: inline-block;
      padding: 5px 0;
    }
    .search_category {
      /* float:left; */
      width: 180px;
    }
    .search_header {
      font-weight: bold;
    }

  </style>
  <!-- BEGIN SEARCH CODE -->
## attributeNames will be used whenever I need to iterate over the list of possible attributes, such as when
## I'm creating all the checkboxes.  You'll notice the two variables below these one are hash tables, which
## do not iterate well (ordering is indeterminate), so I'll use attributeNames as the looper whenever I need to
## use attributeChoices and attributeSelections.
## this variable is a built-in list.
## Beneath the hood, velocity uses a "List" variable for built-in lists.  So here's a documentation page on
## how you may use this variable:  http://docs.oracle.com/javase/6/docs/api/java/util/List.html
  #set($attributeNames = [
  'Crank',
  'Throttle',
  'Transmission Type',
  'Intake Type',
  'Alternator',
  'MAF',
  'CAM'])
## attributeChoices list all of the *possible* choices.  It is used to generate all the checkboxes
## this variable is a hash table of lists ( key = attribute name, value = list of possible choices))
  #set($attributeChoices = {
  'Crank': ['24X','58X'],
  'Throttle': ['DBC','DBW'],
  'Transmission Type': ['4L60E/4L80E','6L80E','T56'],
  'Intake Type':['LS1/LS6','LS2/LS3','Vortec'],
  'Alternator':['Corvette','Truck'],
  'MAF':['LS2','LS3/LS7','07/08 Truck','09 Truck'],
  'CAM':['VTT','No VVT']
  })
## attributeSelections is a hash table of lists.  It contains all of the user's selections.  This variable is
## updated during the routine for determining if a checkbox is checked, and then used later when constructing the
## advanced item search.  It's kind of clunky how this list is populated below, but I wanted to avoid doing a triple
## loop (for each selection ... loop through each possible value ... loop through each http post parameter submitted)
## To avoid the triple, I opportunistically populate it below.
  #set($attributeSelections = {
  'Crank': [],
  'Throttle': [],
  'Transmission Type': [],
  'Intake Type':[],
  'Alternator':[],
  'MAF':[],
  'CAM':[]
  })
</head>
<body>
## I've floated the search to the left, which seems to be a popular place to put it.
<div style='float:left;width:200px;min-height: 100%;margin-right:10px'>
## notice, no action attribute on the form so that it posts back to the document url
  <form method='get'>
  ## if the clear search button was used, set a flag so I can avoid re-checking any checkboxes, effectively clearing them.
    #set($clearForm = false)
  ## the if clause below is a very common way to test for 'null' values.
  ## 1. put the variable inside double quotes.
  ## 2. use the exclamation mark, which prints an empty string "" if the variable doesn't exist or is null
  ## 3. compare that result to an empty string.  If they're equal, the value is null or non-existent.
    #if("$!parameters.get('clearButton')" != "")
      #set($clearForm = true)
    #end
    <input type="text"
           style='width:180px;'
           name="free_search"
           value="$!{parameters.get('free_search')}"/><br>
  ## I have the two buttons right next to each other to make them fix.  you may wish to adjust.
    <input type='submit'
           id="clearButton"
           name="clearButton"
           value='Clear Selections'/><input type='submit'
                                            id="searchButton"
                                            value='Search'/>
    <br>
  ## =========================================================================
  ## begin checkbox code
    #foreach($attr in $attributeNames)
      <div class='search_category'>
        <span class='search_value search_header'>$attr</span>
        #set($attrValues = $attributeChoices.get($attr))
        #foreach($val in $attrValues)
        ## find out if this value is checked by comparing the value with every attr_search parameter submitted.
          #set($paramList = $parameters2.get('attr_search')) ## notice parameters2 (returns list of values)
          #set($valChecked = false)
          #if("$!paramList" != "" && $clearForm == false) ## this is a pretty safe way to see if the paramList is null or not.  don't want to iterate with null object...
            #foreach($param in $paramList)
            ## each checkbox value is a combination of attribute name + values concatenated with a tilde.  To see if a parameter
            ## was submitted, compare the post parameter with the concatenated key string.
            ## if there is a match, I want to do two things.
            ## 1. add the value to my selections variable so I can include it in the advanced search.
            ## 2. set the checkbox to 'checked' so I maintain my state with each page request.
              #if($param == "${attr}~${val}")
              ## add this value to the selected hash of lists which will be used to do the actual search.
              ## first, get a reference to the list of selected values for this attribute
                #set($sel = $attributeSelections.get($attr))
              ## make sure the search attribute exists.  it always should, but this prevents unforeseen change breaks.
              ## unforeseen, such as, you rename an attribute or delete one without properly updating this page.
                #if("$!sel" != "")
                ## it doesn't matter where I put a value in the list
                ## I don't need to put the value at the front, but the method add(pos,element)
                ## returns void, which is preferred to add(element) which returns true/false
                ## If I use just plain add(), I'll print a bunch of 'true' across the page
                  $sel.add(0, $val)
                #end
              ## set the flag so the checkbox is checked.
                #set($valChecked = true)
              #end
            #end ## foreach attr_search parameter
          #end ## if paramList is not null
          <span class='search_value'>
              <label><input type="checkbox"
                            name="attr_search"
                            value="${attr}~$val"
                #if($valChecked) checked='checked' #{end}/> $val</label></span>
        #end ## end-foreach val in attrValues
      </div>
    #end ## end-foreach attr in attributeNames
  ## end checkbox code
  ## =========================================================================

  </form>
</div>

<div style='float:left'>

## perform the search and display the results.
## ALWAYS GOOD TO RESET JUST TO BE SAFE
  $advancedItemSearchManager.resetManager()
  #foreach($attrName in $attributeSelections.keySet())  ## keySet() docs: http://docs.oracle.com/javase/6/docs/api/java/util/Map.html
  ## get a reference to the list of selected/checked values for this attribute
    #set($searchValues = $attributeSelections.get($attrName))
    #if($searchValues && $searchValues.size() > 0) ## if there are any selections for this attribute
    ## always end in .noop() call to avoid printing junk to the screen
    ## notice that I'm using "and()" below.  If you wished to create a search where *any* checked values returned
    ## a hit, you'd want to use "or()" instead.
      $advancedItemSearchManager.and().resetModifiers().required(true).openGroup().noop()
      #foreach ($searchValue in $searchValues)
        $advancedItemSearchManager.attribute($attrName, $searchValue).noop()
      #end
      $advancedItemSearchManager.closeGroup().noop()
    #end
  #end

## here's price range logic if you wish to include it in your search
## PRICE
##  #if ($selectedPriceslength > 0)
##    $advancedItemSearchManager.and().resetModifiers().required(true).openGroup().noop()
##    #foreach ($selectedPrice in $selectedPrices)
##      #if ($selectedPrice == "0-100")
##        $advancedItemSearchManager.costBetween(0, 100).noop()
##      #elseif ($selectedPrice == "101-200")
##        $advancedItemSearchManager.costBetween(100.01, 200).noop()
##      #elseif ($selectedPrice == "201-300")
##        $advancedItemSearchManager.costBetween(200.01, 300).noop()
##      #elseif ($selectedPrice == "301-400")
##        $advancedItemSearchManager.costBetween(300.01, 400).noop()
##      #elseif ($selectedPrice == "401-500")
##        $advancedItemSearchManager.costBetween(400.01, 500).noop()
##      #end
##    #end
##    $advancedItemSearchManager.closeGroup().noop()
##  #end
## SEARCH FIELD - search over the major fields using the search text box value.
  #set($searchTerm = $!{parameters.get('free_search')})
  #if ("$!searchTerm" != "")
    $advancedItemSearchManager.and().resetModifiers().required(true).openGroup().noop()
    $advancedItemSearchManager.searchEachWord(true).noop()
    $advancedItemSearchManager.or().attributes($searchTerm).noop()
    $advancedItemSearchManager.or().barcode($searchTerm).noop()
    $advancedItemSearchManager.or().description($searchTerm).noop()
    $advancedItemSearchManager.or().extendedDescription($searchTerm).noop()
    $advancedItemSearchManager.or().itemId($searchTerm).noop()
    $advancedItemSearchManager.or().manufacturerName($searchTerm).noop()
    $advancedItemSearchManager.or().manufacturerSKU($searchTerm).noop()
    $advancedItemSearchManager.closeGroup().noop()
  #end
  #set($results = $advancedItemSearchManager.search(1, 50))
## Simple output.
<table>
  #foreach ($record in $results.getPageRecords())
    <tr>
      <td>$record.getItemId()</td>
      <td>$record.getDescription()</td>
      <td>$record.getScore()</td>
    </tr>
  #end
</table>
  ## More complex outputting.  Uses some custom PSI snippet includes, so examine carefully before trying to use.
##  #if($results.getPageRecordCount() == 0)
##  ## No items found message here.
##  #else
##
##    #set($groupItems = [])
##    #foreach($rec in $results.getPageRecords())
##      #set($searchItem = $group.getItem($rec.getItemId()))
##    ## sanity check - make sure the item can be retrieved completely -- don't blindly add it to groupItems.
##      #if("$!searchItem" != "")
##        #set($foo = $groupItems.add($group.getItem($rec.getItemId()))) ## set result to foo to avoid outputting 'true'
##      #end
##    #end ##foreach records in search results.
##
##  ## ============================================
##  ## standard group logic
##  ## ============================================
##
##    #set($groupItemsTable = $formatHelper.getTable($groupItems, 4))
##    #if($groupItems && $groupItems.size() > 0)
##      #foreach($row in $groupItemsTable.getRows())
##        <div class="group_row">
##          #foreach($column in $row.getColumns())
##            #if($column)
##              #set($columnUrl = "${baseUrl}$group.path${column.getMerchantItemID()}.html")
##              #ucTemplate("snip_group-column")
##            #end
##          #end
##        </div>
##        <!--/group_row-->
##      #end
##    #end
##
##
##
##  #end ## if there were records
  <!-- END SEARCH CODE -->
</div>
</body>
</html> 

...

Once the template was created, all that was left was to map it to a url.  This was done in the  Home  Catalog   Home  Catalog  Manage Catalog Groups

 

 

Reference

...

The AISM has a large number of methods on it that are used to build up the query and then execute it.  Below are the methods on the object.

Method GroupMethod SignatureNotes
Managementvoid resetManager()Clears all the settings inside of the search manager to prepare for another query.
   
Direct MatchingAdvancedItemSearchManager actualSearch(String actualSearch)Set the text of the actual search query entered by the user to check if they entered a direct item id.
 AdvancedItemSearchManager singleResultDirectMatch(boolean singleResultDirectMatch)If the actual search matches the item id or manufacturer SKU for a result then return a single result.
   
Term ModifiersAdvancedItemSearchManager resetModifiers()Reset the modifiers for the next search term.
 AdvancedItemSearchManager boost(String boost)Set the boost for the search term if there is a match.  Effects the ordering of results.
 AdvancedItemSearchManager fuzzy(Boolean fuzzy)Make search terms fuzzy.
 AdvancedItemSearchManager required(Boolean required)Make the next operation in the search query required
 AdvancedItemSearchManager searchEachWord(boolean searchEachWord)If multiple words are provided in the next term then break them up and search each one.
 

AdvancedItemSearchManager similarity(String similarity)
AdvancedItemSearchManager similarity(double similarity)
AdvancedItemSearchManager similarity(BigDecimal similarity)

Set the similarity for search terms.
 AdvancedItemSearchManager wildcard(Boolean wildcard)Make search terms a wildcard search.
   
TermsAdvancedItemSearchManager attribute(String attributeName, String term)Search a specific attribute for a term.
 AdvancedItemSearchManager attributes(String term)Search all attributes for a term
 

AdvancedItemSearchManager barcode(String term)

Search the barcode field
 

AdvancedItemSearchManager cost(String comparison, String value)
AdvancedItemSearchManager cost(String comparison, double value)
AdvancedItemSearchManager cost(String comparison, BigDecimal value)

Search the cost field.  Valid values for the comparison parameter are:
  • <
  • <=
  • =
  • >=
  • =
 

AdvancedItemSearchManager costBetween(String valueLow, String valueHigh)
AdvancedItemSearchManager costBetween(double valueLow, double valueHigh)
AdvancedItemSearchManager costBetween(BigDecimal valueLow, BigDecimal valueHigh)

Search the cost field to find items between a range.
 

AdvancedItemSearchManager description(String term)

Search the description field
 

AdvancedItemSearchManager extendedDescription(String term)

Search the extended description field.
 

AdvancedItemSearchManager itemId(String term)

Search the item id field.
 AdvancedItemSearchManager manufacturerName(String term)

Search the manufacturer name field.

 AdvancedItemSearchManager manufacturerSKU(String term)Search the manufacturer SKU field.
 

AdvancedItemSearchManager simple(String search)

Performs a "simple" search which is the same behavior that the regular built in search engine of UltraCart uses.
 AdvancedItemSearchManager simple(String search, boolean expanded)Performs a "simple" search which is the same behavior that the regular built in search engine of UltraCart uses, but expand the results further with fuzzy logic.
   
 AdvancedItemSearchManager openGroup()Opens a logic group (think open parenthesis in a programming language)
 AdvancedItemSearchManager closeGroup()Closes a logic group (think closing parenthesis in a programming language)
 AdvancedItemSearchManager and()Separate terms or groups with an AND operation.
 AdvancedItemSearchManager or()Separate terms or groups with an OR operation.
   
Conveniencevoid noop()Consumes the output of the last daisy chained method call to make sure nothing outputs from the Velocity rendering.
   
SearchAdvancedItemSearchResult search()Returns up to 50 results
 AdvancedItemSearchResult search(int pageNumber)Returns specific page number of results with 50 items per page.
 AdvancedItemSearchResult search(int pageNumber, int itemsPerPage)Return specific page number of results with up to 200 items per page.

 

Results

The search methods return an AdvancedItemSearchResult object.  This object contains a child array of AdvancedItemSearchResultRecord which ultimately contains the catalog Item objects.  Below is the object method for each result object.

AdvancedItemSearchResult

MethodNote

String getErrorMessage()

Error message if there was a problem with the query.

int getTotalRecordCount()

Total number of results for the search.

int getPages()

Number of pages in the total result set.

int getPage()

Current page number from the result set.

AdvancedItemSearchResultRecord[] getPageRecords()

Records for this page.

int getPageRecordCount()

Number of records per page.

AdvancedItemSearchResultRecord

MethodNote

String getDescription()

Description of the item
String getItemId()Item ID of the item
float getScore()Score between 0 (lowest) - 1 (highest) of the result
Item getItem()Catalog item object of the item

Sample Queries

In this section we'll look at how to build up some sample queries based a fictional user interface.

...