Add Custom Search Criteria
Add Custom Search Criteria
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 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.
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 extension's GetCapabilities are the spatial predicates (e.g., BBOX, Intersects, Within).
Task 1: 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 INSPIRE metadata schema, as defined by the inspire-iso-19115-definition.xml file.
This customization does not require the Geoportal extension source code; however, you will be creating a new java class in this customization, so java programming knowledge is recommended. If you use an integrated development environment (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 from Additional Options.
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, either by locating it in the definition.xml files (found in the \\geoportal\WEB-INF\classes\gpt\metadata subfolders) or from a profile in the Geoportal's metadata editor interface. In this example, we will add a search filter for the Lineage field of an ISO metadata document to the Additional Optionssearch 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 INSPIRE (Data) profile. Scroll through the form, and find the section titled Quality&Validity. 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 appears in one of the geoportal's default metadata editors, it is likely that this element is already indexed by default. However, if you have created a custom metadata profile with new metadata parameters, or added new metadata elements to the default editors, 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 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.
Task 2: 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>.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.
package gpt.search; 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 = "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 extension 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 extension 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 <!-- Search Beans
-->, 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>gpt.search.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
In order 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.
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.
package gpt.search; 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) { 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, "dc: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"); **/ 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> reference with the <servlet-name> set to RestQueryServlet. Update its <servlet-class> from com.esri.gpt.control.georss.RestQueryServlet to gpt.search.CustomRestQueryServlet, as shown below. Then, save the file.
<servlet> <servlet-name>RestQueryServlet</servlet-name> <servlet-class>gpt.search.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 pop-up interface for Additional Options on the Search page. Now that you've created the filter and done the underlying work to get it referenced 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 where the Modified Date search is defined. You will insert your custom search field code just below the whole <%//modification date %> section. 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.
- After the final </h:panelGroup> tag in the
modification date section, insert the following (substituting your element for the lineage one here):
<% // lineage (added) %> <h:outputText id="txtClearHtml" escape="false" value="<br/>"/> <h:outputText escape="false" value="<h3>"/> <h:outputText id="scLblLineage" value="#{gptMsg['catalog.search.filterLineage.title']}" /> <h:outputText escape="false" value="</h3>"/> <h:inputText id="scLineage" onchange="javascript:updateHiddenValue(this)" value="#{SearchFilterLineage.lineage}" maxlength="4000" styleClass="searchBox" />
- 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> element in the first piece of code you 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}"/>
- Scroll up in the criteria.jsp file to find the javascript function scReadRestUrlParams(). Add your metadata element's (lineage, in our example)
parameters that will be appended to the generated REST URLs:
function scReadRestUrlParams() { … var scLineage = GptUtils.valChkStr( dojo.byId('frmSearchCriteria:scLineageHidden').value); if(scText != "") { restParams += "&lineage=" + encodeURIComponent(scLineage); } … }
- 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. After completing Task 2, 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.