Extensions
Resultgroup extensions
We will present here the implementation of a result group extension class. As a reminder, result groups allow us to organize search results in the form of links, present above the result view.
By default, Xtended Search proposes a grouping by indexes or a grouping by custom tag values; however, we can establish our own logic and develop the associated class.
Here, we want to group the results according to the creation date of the documents:
- Recent (less than 30 days)
- Old (between 30 and 100 days)
- Archived (more than 100 days)
We are going to realize this class by passing in parameters:
- The number of days after which a document is considered “old”.
- The number of days from which a document is considered as “archived”.
Extension class
public class TimeResultGroupExtension implements IResultGroupsExtension {
protected static final Logger log = Logger.getLogger(TimeResultGroupExtension.class);
protected IUser user = null;
protected Search search = null;
protected Map<String, Object> mIndex = null;
protected IPortalModule iPortalModule = null;
public enum eResultGroupsKey {
recentDoc, oldDoc, archivedDoc
}
public void initialize(Map<String, Object> paramMap, ISearchExtension searchExtension, IUser user, Search search) {
this.user = user;
this.search = search;
this.mIndex = paramMap;
iPortalModule = Modules.getPortalModule();
}
public ArrayList<String> getCResultGroupsKeys() throws Exception {
ArrayList<String> cKey = new ArrayList<String>();
try {
cKey.add(eResultGroupsKey.recentDoc.toString());
cKey.add(eResultGroupsKey.oldDoc.toString());
cKey.add(eResultGroupsKey.archivedDoc.toString());
} catch (Exception e) {
String message = e.getMessage();
log.error("Error in TimeResultGroupExtension getCResultGroupsKeys method : " + message);
throw e;
}
return cKey;
}
public String getCustomResultGroupLabel(String key) throws Exception {
String label = "";
try {
label = iPortalModule.getStaticString("xtendedsearch.TimeResultGroupExtension." + key);
} catch (Exception e) {
String message = e.getMessage();
log.error("Error in TimeResultGroupExtension getCustomResultGroupLabel method : " + message);
throw e;
}
return label;
}
public CustomResultGroup getCustomResultGroup(String resultGroupKey, FilterGroup filterGroup, Map<String, SearchElement> searchParameters) throws Exception {
CustomResultGroup customResultGroup = new CustomResultGroup();
try {
SearchElement searchElement = new SearchElement();
searchElement.setNormalized(true);
ArrayList<Object> cValue = new ArrayList<Object>();
Calendar date = new GregorianCalendar();
if (resultGroupKey.equals(eResultGroupsKey.recentDoc.toString())) {
searchElement.setOperator(SearchElement.OPERATOR_AFTER);
String oldCountDay = this.search.getResultGroups().getmParameters().get("OLD_COUNT_DAYS").toString();
date.add(Calendar.DATE, -(Integer.valueOf(oldCountDay)));
} else if (resultGroupKey.equals(eResultGroupsKey.oldDoc.toString())) {
searchElement.setOperator(SearchElement.OPERATOR_BETWEEN);
String archivedCountDay = this.search.getResultGroups().getmParameters().get("ARCHIVED_COUNT_DAYS").toString();
date.add(Calendar.DATE, -(Integer.valueOf(archivedCountDay)));
cValue.add(date.getTime());
date = new GregorianCalendar();
String oldCountDay = this.search.getResultGroups().getmParameters().get("OLD_COUNT_DAYS").toString();
date.add(Calendar.DATE, -(Integer.valueOf(oldCountDay)));
} else if (resultGroupKey.equals(eResultGroupsKey.archivedDoc.toString())) {
searchElement.setOperator(SearchElement.OPERATOR_BEFORE);
String archivedCountDay = this.search.getResultGroups().getmParameters().get("ARCHIVED_COUNT_DAYS").toString();
date.add(Calendar.DATE, -(Integer.valueOf(archivedCountDay)));
}
cValue.add(date.getTime());
searchElement.setValue(cValue);
searchParameters.put("CREATIONDATE", searchElement);
customResultGroup.setcIndex(search.getCIndex());
customResultGroup.setSearchParameters(searchParameters);
} catch (Exception e) {
String message = e.getMessage();
log.error("Error in TimeResultGroupExtension uninitialize method : " + message);
throw e;
}
return customResultGroup;
}
public void uninitialize() {
this.user = null;
this.search = null;
this.mIndex = null;
Modules.releaseModule(iPortalModule);
}
}
Declaration of the class in a search
<search>
<resultGroups extension="com.vdoc.xtendedsearch.resultgroup.extensions.TimeResultGroupExtension" >
<parameters>
<parameter key="OLD_COUNT_DAYS" value="30" />
<parameter key="ARCHIVED_COUNT_DAYS" value="100" />
</parameters>
</resultGroups>
</search>
Source : https://wiki.myvdoc.net/xwiki/bin/view/Dev+Floor/BuildResultGroupsExtensionWithRecentDocuments
Navigation extensions
Use of the “popup” navigation extension
This item is optional. It is possible to define navigation extension classes on a column.
Concretely this feature allows to add an extra icon in the column which will be able to perform a treatment. You can develop your own navigation classes via the SDK.
Navigation extension class :
com.axemble.axvdocsearch.extensions.implementation.INavigationExtension.PopUpNavigationExtension
com.axemble.axvdocsearch.extensions.implementation.navigation.PopUpNavigationExtension
It allows the setting of a link that opens a new search in a popup window. This new search can be filtered according to the result line from which you have navigated.
<navigation name="String">
parameters?*
</navigation>
The name attribute represents the name of the navigation extension class and is mandatory.
Example
<search name="SubWorkFlowInstanceTableSearch"
label="LG_SubWorkFlowInstanceTableSearch"
sourceIndexes="SubWorkFlowInstanceTableIndex"
extension="com.vdoc.SearchExtension"
securityExtension="com.vdoc.ExempleVDocSecurityExtension"
fullTextInput="true"
defaultViewRowsPerPage="10"
linkable="true"
autoExecuteSearch="true">
<customtag name="ChText2" type="text" label="ChText2"/>
<customtag name="nombre" type="number" label="nombre"/>
<view linksTarget="_blank" toolTips="ChText2|nombre" showReference="true" showTitle="false" internalPagination="false">
<column name="ChText2" type="text" label="ChText2">
<navigation name="com.vdoc.brisach.xtendedSearch.navigation.extensions.PlanNavigationExtension"/>
</column>
<column name="nombre" type="number" label="nombre" />
</view>
</search>
Source : https://wiki.myvdoc.net/xwiki/bin/view/Dev+Floor/HowToUsePopupNavigationExtension
Formula extensions
Use of formula extensions
The objective here is to allow the use of formulas in the fields that support it. The use is simple and takes place in 2 steps.
Declaration of formula extensions
Declaration of a list of formula extension : the formula extensions are declared inside the search and will be available inside the whole search.
Example:
<formulaExtensions>
<formulaExtension name="com.axemble.axvdocsearch.extensions.implementation.IFormulaExtension.UserConnectedFormulaExtension"/>
<formulaExtension name="com.axemble.axvdocsearch.extensions.implementation.IFormulaExtension.DatesFormulaExtension"/>
</formulaExtensions>
<formulaExtensions>
<formulaExtension name="com.axemble.axvdocsearch.extensions.implementation.formula.UserConnectedFormulaExtension"/>
<formulaExtension name="com.axemble.axvdocsearch.extensions.implementation.formula.DatesFormulaExtension"/>
</formulaExtensions>
Two formulaExtensions are provided by Process by default:
- “UserConnectedFormulaExtension” which allows to use the connected user.
- “DatesFormulaExtension” which returns the current date.
Using formula extension in XML
After the declaration you can use the default formulas extensions, the formulas must be used as follows ${myformula}.
It is possible to develop your own formula extension via the SDK. See the SDK part dedicated to formula extension.
Formula extensions are currently available in :
Example :
<filterGroups>
<filterGroup default="true" name="testFilterGroup" label="LG_TEST">
<filter
name="sys_Creator" type="text"
operator="equals" value="${user.getFullName()}"
customValue="false" normalized="false" />
</filterGroup>
</filterGroups>
Create a formula extension
The objective here is to propose an example of a formula extension.
We will take as an example a formula extension that calculates a date according to a parameter.
We need to create several searches that have filters on a date. This date must be dynamic, that is; I want to filter the results that have more than x y or :
- x : a relative integer
- y : one of the following elements : [YEAR, MONTH, DAY]
We will probably also need to know the current date.
Syntax of our formula extension
So we need to determine an identifier that will allow us to identify that our formula can evaluate this formula.
Our identifier will be: date
We need two different commands:
- one for the current date which will be “toString()”
- one for the evaluation of a day from today “computeDate(x,y)”
Implementation
A “Base” class allows to simplify the developments:com.axemble.axvdocsearch.extensions.base.implementation.BaseFormulaExtension
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import com.axemble.axvdocsearch.core.engine.CoreIndexer;
import com.axemble.axvdocsearch.extensions.base.implementation.BaseFormulaExtension;
/**
* This class can solve date type formulas. Its prefix is <b>date</b>.
* <ul>
* <li>toString() evaluates the current date</li>
* <li>computeDate(x,y) compute a date based on x,y and the current date (example: ${date.computeDate(-2,MONTH)} the current date minus two months ) :
* <ul>
* <li>where x is a relative integer</li>
* <li>where y is one of the following elements [YEAR, MONTH, DAY]</li>
* </ul>
* </li>
* </ul>
*/
public class DatesFormulaExtension extends BaseFormulaExtension {
/**
* @return String a date with the format intelligible by Lucene
*/
@Override
public Object evaluateFormula(String formula) throws IllegalArgumentException {
if (this.isFormulaHandled(formula)) {
String[] formulaSplited = formula.split("\\.", 2);
if (formulaSplited[1].startsWith("toString")) {
return new SimpleDateFormat(CoreIndexer.INDEX_DATE_FORMAT).format(new Date());
} else if (formulaSplited[1].startsWith("computeDate")) {
try {
// the formula is formated as computeDate(x,y) we need to get x et y
String[] functionSplit = formulaSplited[1].split(",");
// get the x the function split start with "computeDate(" we safe removed it
String xString = functionSplit[0].substring(functionSplit[0].indexOf("(") + 1);
Integer x = 0;
try {
x = Integer.parseInt(xString.replaceAll(" ", ""));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Error on " + DatesFormulaExtension.class.getName() + "can't computeDate the first parameter must be an Integer! for " + xString);
}
// get the y
String fieldString = functionSplit[1].substring(0, functionSplit[1].indexOf(")"));
Integer field = null;
switch (fieldString) {
case "YEAR" :
field = Calendar.YEAR;
break;
case "MONTH" :
field = Calendar.MONTH;
break;
case "DAY" :
field = Calendar.DAY_OF_MONTH;
break;
default :
throw new IllegalArgumentException("Error on " + DatesFormulaExtension.class.getName() + "can't computeDate the second parameter " + fieldString + " is not supported!");
}
// we compute the date
Calendar calendar = new GregorianCalendar();
calendar.add(field, x);
return new SimpleDateFormat(CoreIndexer.INDEX_DATE_FORMAT).format(calendar.getTime());
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("Error on " + DatesFormulaExtension.class.getName() + "can't computeDate malformed formula ${date.computeDate(x,y)}! for " + formula);
}
} else {
throw new IllegalArgumentException("Error on " + DatesFormulaExtension.class.getName() + "can't evaluate : " + formula);
}
} else {
throw new IllegalArgumentException("Error on " + DatesFormulaExtension.class.getName() + "can't evaluate : " + formula);
}
}
/**
* @return "true" for all formulas that begin with "date" otherwise "false".
*/
@Override
public boolean isFormulaHandled(String formula) {
if (formula != null && formula.length() > 0) {
String[] formulaSplited = formula.split("\\.", 2);
if (formulaSplited.length > 1) {
if (formulaSplited[0].equals("date")) {
return true;
}
}
}
return false;
}
}
Sources :