Delivery Tier Fluent Search API 

This feature is available since Hippo CMS 11.1.0.

Introduction

Goal

Create and execute fluent search queries and render the results.

Background

As of Hippo CMS 11.1, Hippo's delivery tier (HST) offers a Fluent Java API through HstQueryBuilder to create and execute searches. Basically this Fluent Builder Style HST-2 Search API provides totally equivalent functionalities as the old style HST-2 Search API provides, but this Fluent Builder Style HST-2 Search API gives a lot more compact and readable code writing to developers.

When an HST query is executed, the HstQuery object is translated into a JCR XPath query, which is executed in the Hippo Repository. The Hippo Repository returns JCR Nodes as hits, that in turn are mapped to HippoBeans in the HstQueryResult object.

Examples

Below, there are examples for a Java HstComponent snippet, JSP and Freemarker snippets and a schematic (not exact!) drawing of how all involved components interact. In the child pages, we explain all steps in more detail.

MySearchComponent Java Code Snippet

Java Class MySearchComponent:

public class MySearchComponent extends BaseHstComponent {

@Override
public void doBeforeRender(final HstRequest request, final HstResponse response) 
                                                   throws HstComponentException {
   HstRequestContext requestContext = request.getRequestContext();
   SearchInfo info = getComponentParametersInfo(request);
   // the scope to search below, for example /content/documents/myhippoproject
   HippoBean scope = requestContext.getSiteContentBaseBean();

   try {
       // parse a free text query to remove invalid chars. The argument
       // 'false' means no wildcards allowed
       String query = getPublicRequestParameter(request, "q");
       String parsedQuery = SearchInputParsingUtils.parse(query, false);

       int pageSize = NumberUtils.toInt(getPublicRequestParameter(request, "ps"), 10);
       int pageNum = NumberUtils.toInt(getPublicRequestParameter(request, "pn"), 1);

       // create the query to search below 'scope', return beans that are
       // of type BaseDocument bean or a subclass/sub-jcr-types, the
       // third argument, 'true', indicates whether to include subtypes
       HstQuery hstQuery = HstQueryBuilder.create(scope)
                .ofTypes(BaseDocument.class)
                .where(constraint(".").contains(parsedQuery))
                .limit(pageSize)
                .offset(pageSize * (pageNum - 1))
                .orderByDescending("mynamespace:date")
                .build();

        // execute the query
        HstQueryResult result = hstQuery.execute();

        // set the result, info and parsedQuery on the HstRequest : It is
        // then available in the JSP
        request.setAttribute("result", result);
        request.setAttribute("info", info);
        request.setAttribute("query", parsedQuery);

    } catch (QueryException e) {
        throw new HstComponentException(
         "Exception occured during creation or execution of HstQuery.", e);
    }
}

Template Code Snippets to Iterate through HippoBeans in the Result

JSP Snippet example using HstQueryResult:

<%@ include file="/WEB-INF/jspf/htmlTags.jspf" %>
<h2>
  <c:out value="${requestScope.info.title}"/> 
  for '<c:out value="${requestScope.query}"/>' :
           ${requestScope.result.totalSize} results
</h2>
<ul>
  <%-- Iterate through the hippoBeans on the result --%>
  <c:forEach var="item" items="${requestScope.result.hippoBeans}">
    <hst:link var="link" hippobean="${item}"/>
    <li class="overview-item">
      <hst:cmseditlink hippobean="${item}"/>
      <a href="${link}">${item.title}</a>
      <div>
        <c:if test="${hst:isReadable(item, 'date.time')}">
          <p><fmt:formatDate value="${item.date.time}"
                             type="Date" pattern="MMMM d, yyyy h:mm a"/></p>
        </c:if>
        <p>${item.summary}</p>
      </div>
    </li>
  </c:forEach>
</ul>

Freemarker snippet:

<#include "/WEB-INF/freemarker/include/imports.ftl">
<h2>
  ${info.title?html} for '${query?html}': ${result.totalSize} results
</h2>
<ul>
  <#-- Iterate through the hippoBeans on the result -->
  <#if result?? && result.hippoBeans?has_content>
    <#list result.hippoBeans as item>
      <@hst.link var="link" hippobean=item />
      <li class="overview-item">
        <@hst.cmseditlink hippobean=item/>
        <a href="${link}">${item.title?html}</a>
        <div>
          <#if item.date?? && item.date.time??>
            <p><@fmt.formatDate value=item.date.time 
                     type="Date" pattern="MMMM d, yyyy h:mm a"/>
            </p>
          </#if>
          <p>${item.summary?html}</p>
        </div>
      </li>
    </#list>
  </#if>
</ul>