Searching Using Struts

The Lucy demo publication's search template contains the following:

...taglib declarations...

<TEMPLATE:call file="searchForm_simple.jsp" />

...error handling code...

<TEMPLATE:call file="result.jsp" />

The first included file, searchForm_simple.jsp carries out steps 1 and 2 of the search process (constructing a search expression and executing it), while the second file, result.jsp, presents the results of the search. This workflow is also defined in the publication's Struts configuration file, struts-config.xml. Here is an extract from this file:

    <action path="/search/simple"
            parameter="method"
            type="com.escenic.search.SearchAction"
            name="SimpleSearchForm"
            input="/template/searchForm_simple.jsp"
            scope="session">
      <forward name="success"
               path="/template/result.jsp"
               redirect="false"/>
    </action>

For a proper introduction to Struts, see http://struts.apache.org/primer.html. In short, however, the above code:

  • Defines searchForm_simple.jsp as a SimpleSearchForm

  • Specifies that the data input to the form will be processed by the Struts action /search/simple, which is an action of type com.escenic.search.SearchAction.

  • Specifies that output from the /search/simple action will be directed to result.jsp.

Here is the contents of searchForm_simple.jsp:

...taglib declarations...

<HTML:form action="/search/simple">  
  <input type="hidden" name="successUrl" value="/template/common.jsp" />
  <input type="hidden" name="errorUrl" value="/template/common.jsp" />
  <HTML:hidden property="pageLength" value="5" />

  <input type="hidden" name="publicationId" value="<BEAN:write name="publication" property="id" />" />
  <HTML:hidden property="searchEngineName" value="LucySearchEngine" />
  <HTML:hidden property="includeSubSections" value="true" />

  <SECTION:use uniqueName="ece_frontpage">
    <BEAN:define id="secId" name="section" property="id" toScope="request"/>
  </SECTION:use>
  <BEAN:define id="allSecId" name="secId" scope="request" />
  <HTML:hidden property="includeSectionId" value="<%= String.valueOf(allSecId) %>" />

  <HTML:hidden property="articleType" value="default" />
  <HTML:hidden property="sortString" value="score" />
  <HTML:text property="searchString" />
  <HTML:submit />
</HTML:form>

The items to pay particular attention to here are:

  • The HTML, and BEAN prefixes reference Struts tag libraries declared at the top of the file.

  • The SECTION prefix references an Escenic tag library, also declared at the top of the file.

  • All of the form's HTML:hidden and HTML:text fields are bound to form properties that define search parameters. For descriptions of these properties, see the descriptions of SearchForm, ArticleSearchForm and SimpleSearchForm in the Escenic Content Engine Struts Form Reference.

  • The HTML:hidden, are, as the name suggests, hidden: they are not displayed on the page. The form displayed in the publication therefore consists of a single input field (searchString) and a Submit button.

  • LucySearchEngine is the name of the default search engine instance used by Content Studio. If you have created another search engine instance that you want to use (see Set LucySearchEngine Properties), then you should specify the name of this instance in the searchEngineName property. For example:

    <HTML:hidden property="searchEngineName" value="AnotherLucyInstance" />

When the publication reader enters a string in the field and clicks on Submit, the /search/simple action is executed and returns a set of com.escenic.search.ResultPage beans, each representing a page of search results. The search form's pageLength property (see above) determines how many results each page contains, and therefore also how many result pages are returned.

The demo publication's result.jsp template simply contains code for cycling through the returned ResultPage beans and displaying their contents:

<div class="result">
  <LOGIC:present name="com.escenic.search.ResultPage">
    <LOGIC:equal value="0" name="com.escenic.search.ResultPage" property="totalHits">
      <h3>No articles found.</h3>
    </LOGIC:equal>
    <LOGIC:greaterThan value="0" name="com.escenic.search.ResultPage" property="totalHits">
      <div class="display">
        Showing <BEAN:write name="com.escenic.search.ResultPage" property="fromHits" />
              - <BEAN:write name="com.escenic.search.ResultPage" property="toHits" />
              of <BEAN:write name="com.escenic.search.ResultPage" property="totalHits" />
      </div>
      <hr/>
      <ul>
        <LOGIC:iterate id="result" name="com.escenic.search.ResultPage" type="com.escenic.search.Result">
          <li>
            <h3>
              <a href="<BEAN:write name="result" property="url" />">
                <BEAN:write name="result" property="field(title)" />
              </a>
            </h3>
            <p>
              <LOGIC:notEmpty name="result" property="description">
                <BEAN:write name="result" property="description" />
              </LOGIC:notEmpty>
            </p>
            <p>
              <a href="<BEAN:write name="result" property="url" />">Linktext</a>
            </p>
            <hr/>
          </li>
        </LOGIC:iterate>
      </ul>
      <div class="navigator">
        <UTIL:notEqual name="com.escenic.search.ResultPage" property="pageNumber" value="1">
          <a href="<BEAN:write name="com.escenic.search.ResultPage" property='<%= "url[1]"%>' />">First</a>
        </UTIL:notEqual>
        <BEAN:define id="pageNr" 
                     name="com.escenic.search.ResultPage" property="pageNumber" type="Integer" />
        <BEAN:define id="pages" 
                     name="com.escenic.search.ResultPage" property="numberOfPages" type="Integer" />
        <UTIL:loop id="pageNumber" from="1" to="<%= pages %>">
          <UTIL:equal name="pageNumber" value="<%= pageNr.toString() %>">
            <UTIL:notEqual name="com.escenic.search.ResultPage" property="pageNumber" value="1">
              |
            </UTIL:notEqual>
            <strong>
              Page <BEAN:write name="com.escenic.search.ResultPage" property="pageNumber" /> 
              of <BEAN:write name="com.escenic.search.ResultPage" property="numberOfPages" />
            </strong>
          </UTIL:equal>
          <UTIL:notEqual name="pageNumber" value="<%= pageNr.toString() %>">
            | 
            <a href="<BEAN:write name="com.escenic.search.ResultPage" 
                                 property='<%= "url[" + pageNumber + "]"%>' />">
              <BEAN:write name="pageNumber" />
            </a>
          </UTIL:notEqual>
        </UTIL:loop>
        <UTIL:notEqual name="com.escenic.search.ResultPage" property="pageNumber" value="<%= pages.toString() %>">
          |
          <a href="<BEAN:write name="com.escenic.search.ResultPage" 
                               property='<%= "url[" + pages + "]"%>' />">
            Last
          </a>
        </UTIL:notEqual>
      </div>
    </LOGIC:greaterThan>
  </LOGIC:present>
</div>

Each ResultPage bean has a result property containing an array of com.escenic.search.Result beans, each of which contains one of the search results returned by Lucy. The Result bean's url property contains the URL of a content item, while the description property contains the result description (an extract from the content item). The field property contains all the content item's fields. In the example above, the content items' title fields are retrieved and used as titles for each result.

For full descriptions of the ResultPage and Result beans, see the Escenic Content Engine Bean Reference.