Skip to content

Add Custom Search Criteria

eggwhites edited this page Jan 2, 2013 · 21 revisions

In the geoportal, it is possible to filter search results based on certain metadata elements. For example, you can retrieve documents with specific terms in the title by prefixing the search query in the Search field with title:searchTerm. This is enabled by the Lucene index, which classifies information in a metadata element with a heading by which a user would search. Search syntax is further discussed in Using Lucene Search Text Queries.

It is also possible to add this customized element search as a search filter in the list of available search options shown in the Additional Options dialog on the Search page. This topic will first describe how to designate a specific element for search, and then how to add this search to the Additional Options dialog as a custom search filter.

Note: When you add the customized search criteria, you can also search the custom field using lucene syntax in the CS-W interface. However, adding the custom field does not alter the geoportal's GetCapabilities operation; the only properties explicitly listed in the geoportal's GetCapabilities are the spatial predicates (e.g., BBOX, Intersects, Within).

Steps for how to add a custom search criteria are discussed in this topic.

Table of Contents

Designate a Specific Element for Search and Verify that it is Indexed

This section assumes that you have an initial understanding of the geoportal's property-meanings.xml file as described in Details of Lucene Indexing in the Geoportal. In this example, you will configure the geoportal to index an element from the ISO metadata schema, as defined by the \\geoporta\WEB-INF\classes\gpt\metadata\iso\apiso-indexables.xml file.

Tip: This customization does not require the Geoportal Server source code; however, you will be creating a new java class in this customization, so basic java programming knowledge is highly recommended. If you use an IDE (such as Eclipse) to compile the new class, remember to import the geoportal.war file into your project as you develop.

Identify the element you want to be searchable on the Search Page

Likely you already have in mind a metadata element that your organization would like to be searchable from the Additional Options interface. It is important to be able to locate this element in one or more metadata profiles your geoportal supports. Verify that you can find the metadata element in one of the indexables files (e.g., \\geoporta\WEB-INF\classes\gpt\metadata\iso\apiso-indexables.xml). In this example, we will add a search filter for the Lineage field of an ISO metadata document to the Additional Options search interface. To see the Lineage field in the geoportal metadata editor, launch the geoportal and log in as a publisher user or administrator. Then, click the Administration tab, and then the Add link. Select the Use dedicated editor to create metadata manually option, and then choose the ISO 19115 profile. In the resulting form, you should see a section titled Quality. In this section, you will see the Lineage metadata element.

Determine if the chosen element is already indexed by default.

If the element that you want to search from the Additional Options interface already appears in one of the geoportal's indexables.xml files, then it indexed by default. However, if you have created a custom metadata profile or added new metadata elements to the default profiles, then you may need to define the indexing for the element. Follow guidance in the "Determine if the chosen element is already indexed by default" section in the Details of Lucene Indexing in the Geoportal topic. After reading that section and carrying out recommendations for how the chosen element is indexed, you should be able to input a lucene query for your element in the search field on the geoportal Search page and retrieve relevant results.

Add the Dublin Core reference element to the property-meaning

It is necessary to ensure that your indexed element is available through CS-W. To do this, you will need to verify that the element had a dublin core mapping in the property-meanings.xml file. If it doesn't already have one, you will need to add it in. In our example, we give the lineage element a dublin core name and an alias, as shown below:

 <property-meaning name="apiso.Lineage" valueType="String" comparisonType="keyword">
    <apiso name="apiso:Lineage"/>
  <dc name="dc:lineage" aliases="lineage"/>
  </property-meaning>  

Add the New Search Element to the Additional Options Dialog

In this task, you will prepare your search element for search and display in the Additional Options dialog. Note that in this example, our new searchable element, lineage, is a text field. If your new element is a date field or a multiselect field, the steps below should be adapted to the type of the field.

Build a new class for the new search filter

The geoportal compiled code already includes classes for the default search options seen in the Additional Options dialog. To include your additional filter in the dialog, you will need to create a new class that provides information to the geoportal about your element. The new class must implement the ISearchFilter interface, or extend/implement one of the children of ISearchFilter. You can use any name for your new class, but if you want to follow the naming convention of the other search filter classes within the geoportal, the name of your class would be SearchFilter<name_of_your_indexing_field></name_of_your_indexing_field>.java. The code shown below is an example of a class that could be used for adding our example lineage element. After you author and compile the new class, put the resulting class file into the \\geoportal\WEB-INF\classes\gpt\search\ directory.

import com.esri.gpt.catalog.search.ISearchFilter; 
import com.esri.gpt.catalog.search.SearchException;
import com.esri.gpt.catalog.search.SearchParameterMap; 
import com.esri.gpt.catalog.search.SearchParameterMap.Value;
import com.esri.gpt.framework.util.Val;
@SuppressWarnings("serial") 
public class SearchFilterLineage implements ISearchFilter { 

// key to be used to serialize class to a map 
private static String KEY_LINEAGE = "apiso.Lineage"; 

// instance variable 
private String lineage;

// property (Can be used by jsf(advanced search page) 
public String getLineage() { 
return Val.chkStr(lineage);
} 
// property (Can be used by jsf(advanced search page) 
public void setLineage(String lineage) { 
this.lineage = lineage; 
} 
// Serialize class instance into a map 
public SearchParameterMap getParams() { 
SearchParameterMap map = new SearchParameterMap(); 
map.put(KEY_LINEAGE, map.new Value(this.getLineage(), ""));
return map;
}
// The class may receive a new map for deserialization (e.g. saved searches 
// can trigger this 
public void setParams(SearchParameterMap parameterMap) throws SearchException { 
Value val = parameterMap.get(KEY_LINEAGE);
this.setLineage(val.getParamValue()); 
}
// Deep comparison of filters 
public boolean isEquals(Object obj) {
if (obj instanceof SearchFilterLineage) { 
return ((SearchFilterLineage) obj).getLineage().equals(this.getLineage()); 
} 
return false; 
} 
// This will be called by the clear button 
public void reset() { 
this.setLineage(""); 
} 
// Before search, validate will be called. An exception can be thrown 
// that will stop the search and the error is displayed on the search page 
public void validate() throws SearchException {
if (this.getLineage().equals("this should throw an exception")) {
throw new SearchException("this should throw an exception");
} 
} 
}

Store instances of your new class in the geoportal session variables

Search parameters and their values are stored in session variables. These variables are created when a user loads the first web page of the site, and the variables then persist till the user closes the browser or does not create any web requests for a certain amount of time. The JavaServer Faces framework, upon which the Geoportal Server is built, has a configuration file where session variables are stored. This file is located in the \\geoportal\WEB-INF directory, and is called gpt-faces-config.xml. You will need to update this file in two places.

  • Under the section titled , add the following new managed bean to store your new variables in the session. Note that in the example below, we reference our example Lineage element; you will need to edit this to match the element for which you are customizing the search:
<!--managed bean for lineage search-->
<managed-bean>
<description>Search Filter with lineage properties</description>
<managed-bean-name>SearchFilterLineage</managed-bean-name>
<managed-bean-class>SearchFilterLineage</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
  • In the managedProperty called miscelleniousFilters, you will need to make some edits. Verify that the value-class is set to com.esri.gpt.catalog.search.ISearchFilter. In the list of values, add a value that references your new managed bean. In the example below, we add the line <value>#{SearchFilterLineage}</value>:
<managed-property>
  <property-name>miscelleniousFilters</property-name>
  <property-class>com.esri.gpt.catalog.search.SearchFiltersList</property-class>
  <list-entries>
    <value-class>com.esri.gpt.catalog.search.ISearchFilter</value-class>
    <value>#{SearchFilterHarvestSites}</value>
    <value>#{SearchFilterLineage}</value>
  </list-entries>
</managed-property>

Override the Query Servlet Class

For your custom search to work from the search page, it must also work from the REST URL. The RestQueryServlet class is the controller for REST searches and should be overridden. The code below is shown for creating the class that will override the RestQueryServlet class. Open up a text editor and copy the lines below into it. Save the file as CustomRestQueryServlet.java, in the \\geoportal\WEB-INF\classes\gpt\search folder.

Note: In this example, you see that lineage is the name of the rest queryable. You will need to update this for the element you want to search.

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import com.esri.gpt.catalog.discovery.rest.RestQuery;
import com.esri.gpt.catalog.discovery.rest.RestQueryParser;
import com.esri.gpt.catalog.search.SearchCriteria;
import com.esri.gpt.control.georss.RestQueryServlet;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.util.Val;
public class CustomRestQueryServlet extends RestQueryServlet {
private static String REST_PARAM_KEY = "lineage";
//Relate the rest queryable to the CSW queryables 
protected RestQuery parseRequest(HttpServletRequest request, RequestContext context) {
Logger LOG = Logger.getLogger(RestQuery.class.getCanonicalName());
	RestQuery query = super.parseRequest(request, context);
RestQueryParser parser = new RestQueryParser(request,context,query);
// "lineage" will be the name of the rest queryable
parser.parsePropertyIsLike(REST_PARAM_KEY, "apiso.Lineage");
/** The below is shown as an example
parser.parseRepositoryId("rid");
parser.parseResponseFormat("f");
parser.parseResponseGeometry("geometryType");
parser.parseResponseStyle("style");
parser.parseResponseTarget("target");
parser.parseStartRecord("start",1);
parser.parseMaxRecords("max",10);
parser.parsePropertyIsEqualTo("uuid","uuid");
parser.parsePropertyIsLike("searchText","anytext");
parser.parsePropertyList("contentType","dc:type",",",true);
parser.parsePropertyList("dataCategory","dc:subject",",",true);
parser.parsePropertyRange("after","before","dct:modified");
parser.parseSpatialClause("bbox","spatialRel","geometry");
parser.parseSortables("orderBy");
**/
LOG.log(Level.FINER, "In Custom Rest Query Servlet");

return query;
}
//Populate the searchCriteria with the rest queryable
protected SearchCriteria toSearchCriteria(HttpServletRequest request, 
RequestContext context, RestQuery query) {
SearchCriteria criteria = super.toSearchCriteria(request, context, query);
RestQueryParser parser = new RestQueryParser(request,context, query);
String sLineage = Val.chkStr(parser.getRequestParameter(REST_PARAM_KEY));
if (sLineage.length() > 0) {
SearchFilterLineage filterLineage = new SearchFilterLineage();
filterLineage.setLineage(sLineage);
criteria.getMiscelleniousFilters().add(filterLineage);
}
return criteria;
}
}

Update geoportal's web.xml to point to your new queryServlet Class

Navigate to your \\geoportal\WEB-INF folder and open the web.xml file in a text editor. Find the <servlet></servlet> reference with the <servlet-name></servlet-name> set to RestQueryServlet. Update its <servlet-class> from com.esri.gpt.control.georss.RestQueryServlet to CustomRestQueryServlet, as shown below. Then, save the file.

<servlet>
  <servlet-name>RestQueryServlet</servlet-name>
  <servlet-class>CustomRestQueryServlet</servlet-class>
  <init-param>
    <param-name>bundleBaseName</param-name>
    <param-value>gpt.resources.gpt</param-value>
  </init-param>
  <load-on-startup>6</load-on-startup>
</servlet>

Adapt the Additional Options web page

The criteria.jsp file defines the search criteria fields on the Search page, including the pop-up interface for Additional Options. Now that you've created the filter and done the underlying work to reference it in the geoportal, it is important to add the field to this search interface. Follow the steps below.

  • Navigate to the \\geoportal\catalog\search directory and open the criteria.jsp file in a text editor.
  • In the criteria.jsp file, find the section that defines the function scReadRestUrlParams():
 function scReadRestUrlParams() {
    var restParams = "";
    var bIsRemoteCatalog = scIsRemoteCatalog();
...
  • Update this with a new variable that will add your metadata element's parameters (e.g., lineage, in our example) to be appended to the generated REST URLs, such that the function now looks like this:
function scReadRestUrlParams() {
    var restParams = "";
    var bIsRemoteCatalog = scIsRemoteCatalog();      
    var scLineage = GptUtils.valChkStr(
dojo.byId('frmSearchCriteria:scLineageHidden').value);
if(scLineage != "") {
restParams += "&lineage=" + encodeURIComponent(scLineage)+"&";
}
  • Scroll down to the <%//modification date %> section. You will insert your custom search field code just below the whole <%//modification date %> section, after the final &lt;/h:panelgroup&gt;</servlet-class> tag in the modification date section. Find this section and insert the following (substituting your element for the lineage one here):
<% // lineage (added) %>
<h:outputText escape="false" value="<nowiki><h3></nowiki>"/>
<h:outputText id="scLblLineage" value="#{gptMsg['catalog.search.filterLineage.title']}" />
<h:outputText escape="false" value="<nowiki></h3></nowiki>"/>
<h:inputText id="scLineage" onchange="javascript:updateHiddenValue(this)" value="#{SearchFilterLineage.lineage}" maxlength="4000" styleClass="searchBox" />
  • Note that the value defined for the outputText id=scLbl will be a string that will need to be referenced in the gpt.properties file. This string defines the label for your field in the Additional Options interface; we add this string to the gpt.properties file in the next step after making changes to the criteria.jsp file.
  • Near the end of the criteria.jsp file, there is a section that further defines the search options on the Additional Options dialog. Add a value for your newly added search option, beneath the h:inputHidden id="scSelThemeHidden" tag in the list, as shown below. Note that the id for your inputHidden tag should be similar to the id in the <h:inputtext></h:inputtext> element in the piece of code you just added to this file. In our example for the inputText element, the id was scLineage. In this h:inputHidden element, the id will be scLineageHidden:
 <h:outputText escape="false" value="</div>"/> 
 <h:inputHidden id="scSelSortHidden" value="#{SearchFilterSort.selectedSort}"/> 
 <h:inputHidden id="scDateToHidden" value="#{SearchFilterTemporal.dateModifiedTo}"/> 
 <h:inputHidden id="scDateFromHidden" value="#{SearchFilterTemporal.dateModifiedFrom}"/> 
 <h:inputHidden id="scSelContentHidden" value="#{SearchFilterContentTypes.selectedContentType}"/> 
 <h:inputHidden id="scSelThemeHidden" value="#{SearchFilterThemeTypes.selectedThemes}"> 
 <h:inputHidden id="scLineageHidden" value="#{SearchFilterLineage.lineage}"/>
  • Save the criteria.jsp file.

Update gpt.properties with a label for your new search filter

  • Navigate to the \\geoportal\WEB-INF\classes\gpt\resources directory and open the gpt.properties file in a text editor.
  • Search for the section where search filters are defined. Keys for search filters begin with the string catalog.search.filter
  • Add a new value. This value should match the scLbl string you defined in your h:outputText id=scLbl element from the criteria.jsp file. In our example, we add the following:
 catalog.search.filterLineage.title = Lineage
  • Save the gpt.properties file.
Restart the geoportal web application for your changes to take affect. You should be able to launch the Additional Options dialog and see a field for your new search filter. When you input text for that filter and click ok, then click the search button, results should contain your input text in that element of the metadata document.
Clone this wiki locally