Custom Search Filter - Bloomreach Experience - Open Source CMS
06-12-2019

Custom Search Filter

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

Introduction

The Document search application allows you to search on all fields of your documents types. Out-of-the-box, Advanced Search shows filters for generic document metadata fields such as document type and location. The Advanced Search feature 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 myproject: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: myproject: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, configure 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:configuration/hippo:translations/hippo:cms
  /advanced-search-filters:
    jcr:primaryType: hipposys:resourcebundles
    /en:
      jcr:primaryType: hipposys:resourcebundle
      titlePropertyFilter.propertyName: Title
      titlePropertyFilter.title: Title Filter
    /nl:
      jcr:primaryType: hipposys:resourcebundle
      titlePropertyFilter.propertyName: Titel
      titlePropertyFilter.title: Titelfilter

Extending the Generic Property Filter

The built-in generic property filter, com.onehippo.cms7.search.frontend.filters.GenericPropertyFilterPlugin, is a generic 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 Document search application 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, 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?