Custom Search Filter - BloomReach Experience - Open Source CMS

This article covers a Hippo CMS version 12. There's an updated version available that covers our most recent release.

27-08-2018

Custom Search Filter

This Bloomreach Experience Manager feature requires a standard or premium license. Please contact BloomReach for more information.

Introduction

The Advanced Search Perspective allows you to search on all fields of your documents types. Out-of-the-box, the Advanced Search Perspective shows filters for generic document metadata fields such as document type and location. The Advanced Search Perspective can easily be customized by adding more search plugins that filter on specific properties of your documents types.

You can either configure the built-in generic property filter plugin for simple extra filtering purpose, or implement your own filter plugin by extending the generic property filter plugin or implementing a new filter plugin.

Configuring the Generic Property Filter

The Generic Property Filter allows the user to enter a text string that is used as a filter on a property of type String or Long. Below you see an example filtering on the document Title:

To configure this, first you need to determine the property in your content model you want to filter on; in this case the filter property is the  hippogogreen:title property. Then, you can add the following repository configuration under  /hippo:configuration/hippo:frontend/cms/cms-advanced-search node:

/hippo:configuration/hippo:frontend/cms/cms-advanced-search:
  /titlePropertyFilter:
    jcr:primaryType: frontend:plugin
    plugin.class: com.onehippo.cms7.search.frontend.filters.GenericPropertyFilterPlugin
    wicket.id: ${search.extensions}
    wicket.model: ${model.search}
    filterPropertyName: hippogogreen:title
    filterPropertyType: text

Also, please note that the labels ("Title Filter" and "Title") shown in the screenshot above can be configured as repository resource bundle labels.

To do that, load the following translations. The GenericPropertyFilterPlugin uses the name of the initialize item (titlePropertyFilter) from the initialize item above to look up the translations for the property name and title field below:

{
  "hippo:cms": {
    "advanced-search-filters": {
      "en": {
        "titlePropertyFilter.propertyName": "Title",
        "titlePropertyFilter.title": "Title Filter"
      },
      "nl": {
        "titlePropertyFilter.propertyName": "Titel",
        "titlePropertyFilter.title": "Titel Filter"
      }
    }
  }
}

Extending the Generic Property Filter

The built-in generic property filter,  com.onehippo.cms7.search.frontend.filters.GenericPropertyFilterPlugin, is a simple filter plugin implementation which allows users to enter a text string that is then used to filter the search result based on a specific document property.

For simplicity, the generic property filter plugin can handle only two property types currently:

  • String : If the specified property is type of String, then the filter uses "jcr:contains" operator to filter the search result.
  • Long: If the specified property is type of Long, then the filter uses "Equals" operator to filter the search result.

For your information, the above is implemented like this:

    @Override    
    public List<Constraint> getConstraints() {
        List<Constraint> constraints = new LinkedList<Constraint>();

        if (StringUtils.isNotBlank(getFilterPropertyName()) && StringUtils.isNotBlank(getFilterPropertyValue())) {
            if (FILTER_PROPERTY_TYPE_INTEGER.equals(getFilterPropertyType())) {
                constraints.add(QueryUtils.integer(getFilterPropertyName()).isEqualTo(NumberUtils.toInt(getFilterPropertyValue())));
            } else {
                constraints.add(QueryUtils.text(getFilterPropertyName()).contains(getFilterPropertyValue()));
            }
        }

        return constraints;
    }

So, if you want a more sophisticated filter plugin based on some other property, then you may create a new class extending the  GenericProperyFilterPlugin. In most cases, you can simply override the  #getConstraints() method.

Implementing a More Complex Custom Search Filter

If you need a more complex search filter, not just using single property, or using a different input mechanism, then you should implement a custom search filter.

Custom constraint providers can be implemented by creating a plugin that implements the  IConstraintProvider interface:

MyDocumentsPlugin.java:

package org.example;

import java.util.LinkedList;
import java.util.List;
import com.onehippo.cms7.search.frontend.ISearchContext;
import com.onehippo.cms7.search.frontend.constraints.IConstraintProvider;
import com.onehippo.cms7.search.frontend.constraints.YesNoNeither;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.model.CompoundPropertyModel;
import org.hippoecm.frontend.plugin.IPluginContext;
import org.hippoecm.frontend.plugin.config.IPluginConfig;
import org.hippoecm.frontend.service.render.RenderPlugin;
import org.hippoecm.frontend.session.UserSession;
import org.onehippo.cms7.services.search.query.QueryUtils;
import org.onehippo.cms7.services.search.query.constraint.Constraint;
import org.onehippo.cms7.services.search.query.constraint.TextConstraint;

public class MyDocumentsPlugin extends RenderPlugin
                               implements IConstraintProvider {
    private String authoredByMe;

    public MyDocumentsPlugin(final IPluginContext context,
                             final IPluginConfig config) {
        super(context, config);
        Form form = new Form("form", new CompoundPropertyModel(this));
        form.add(new YesNoNeither("authoredByMe") {

            @Override
            protected void onModelChanged() {
                super.onModelChanged();
                updateSearchResults();
            }
        });
        add(form);
    }

    private void updateSearchResults() {
        ISearchContext searcher = getPluginContext().getService(
                                        ISearchContext.class.getName(),
                                        ISearchContext.class);
        searcher.updateSearchResults();
    }

 @Override
    public List<Constraint> getConstraints() {
        List<Constraint> constraints = new LinkedList<Constraint>();
        if (authoredByMe != null) {
            final TextConstraint iamTheUser =
                         QueryUtils.text("hippostd:holder").isEqualTo(
                            UserSession.get().getJcrSession().getUserID());
            if ("yes".equals(authoredByMe)) {
                constraints.add(iamTheUser);
            } else {
                constraints.add(QueryUtils.not(iamTheUser));
            }
        }
        return constraints;
    }

    @Override
    public void clearConstraints() {
        authoredByMe = null;
        redraw();
    }
}

The YesNoEither component is provided by the advanced search plugin api; the    ISearchContext can be used to trigger an update of the shown results.

As shown, the plugin implements the constraint provider interface. It's methods are invoked when a new query is built ( getConstraints) or all constraints should be reset ( clearConstraints).

The markup to go along with this code:

MyDocumentsPlugin.html:

<html xmlns:wicket="http://wicket.apache.org/">
<wicket:panel>
  <div class="hippo-advanced-search-filter">
    <form wicket:id="form">
      <ul>
        <li class="filter">
          <h3>Author</h3>
          <ul>
            <li style="clear: both; margin-top: -15px;">
              <span class="hippo-advanced-search-radio">
                <span class="hippo-advanced-search-select">
                  <wicket:message key="yes">Y</wicket:message>
                </span>
                <span class="hippo-advanced-search-select">
                  <wicket:message key="no">N</wicket:message>
                </span>
                <span class="hippo-advanced-search-reset"></span>
              </span>
            </li>
            <li style="clear: both;">
              Authored by Me
              <span wicket:id="authoredByMe"
                    class="hippo-advanced-search-radio">
                <input type="radio" wicket:id="authoredByMe-yes"
                       class="hippo-advanced-search-select"/>
                <input type="radio" wicket:id="authoredByMe-no"
                       class="hippo-advanced-search-select"/>
                <span class="hippo-advanced-search-reset">
                  <img wicket:id="authoredByMe-reset"/>
                </span>
              </span>
            </li>
          </ul>
        </li>
      </ul>
    </form>
  </div>
</wicket:panel>
</html>

These plugins need to be registered in the advanced search perspective at /hippo:configuration/hippo:frontend/cms/cms-advanced-search:

/hippo:configuration/hippo:frontend/cms/cms-advanced-search:
  /myDocuments:
    jcr:primaryType: frontend:plugin
    plugin.class: org.example.MyDocumentsPlugin
    wicket.model: ${model.search}
    wicket.id: ${search.extensions}

Make sure that if you add the    MyDocumentsPlugin.java and    MyDocumentsPlugin.html to the cms project that you make sure that the    .html (and    .properties if you    I18N your custom search filter) end up in the jar. You can do this by making sure the cms    pom.xml build contains :

<build>
    <finalName>cms</finalName>
    <resources>
      <resource>
        <filtering>false</filtering>
        <directory>${basedir}/src/main/java</directory>
        <includes>
          <include>**/*.html</include>
          <include>**/*.properties</include>
        </includes>
      </resource>
    </resources>
    .....

If you want your plugin to be ordered at a  higher location, just move your  myDocuments node up in the repository configuration at  /hippo:configuration/hippo:frontend/cms/cms-advanced-search

The end result:

Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?