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);
	}
}
<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

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 :