2012-09-27 70 views
3

可以說我擁有一個ActivePivot立方體,事實上只包含Value和Currency。 可以說我的魔方具有貨幣作爲常規維度。ActivePivot葉級別聚合和分析維

我們用多種貨幣的事實填充立方體。

我們有一個外匯服務,需要貨幣和參考貨幣來獲得利率。

現在,Value.SUM沒有任何意義,我們將不同貨幣的值相加,因此我們希望有一個後處理器可以將所有值轉換爲參考貨幣,例如USD,然後將它們相加,因此我們編寫了一個後置處理器,用於擴展ADynamicAggregationPostProcessor,將貨幣指定爲葉級別維度,並使用外匯服務進行轉換,我們很高興。

但是,可以說我們不想僅轉換爲美元,我們希望轉換爲10種不同的貨幣,並在屏幕上看到彼此相鄰的結果。 因此,我們創建了一個分析維度,稱爲ReferenceCurrency,其中包含10個成員。

我的問題是:如何更改上述後處理器來處理分析維?普通香草ADynamicAggregationPostProcessor不處理分析維度,只有默認成員對此後處理器可見。處理分析維度的其他後處理程序(如DefaultAggregatePostProcessor)沒有指定葉級別的方法,因此我無法通過貨幣獲取聚合,因此無法執行外匯轉換。我怎樣才能得到我的蛋糕並且吃它呢?

回答

1

它看起來像你想同時使用ActivePivot的兩個高級功能(分析維度來暴露相同聚合的多個結果,動態聚合到以不同貨幣表示的聚合數量)。

另外每一個都很容易通過配置和幾行代碼注入來設置。但是爲了交錯兩者,您需要了解後處理器評估的內幕,並在正確的位置注入業務邏輯。

以下是基於ActivePivot 4.3.3的示例。它已經在開源Sandbox應用程序中編寫,因此您可以在將其調整到自己的項目之前快速運行它。

首先,我們需要一個簡單的分析尺寸以保持可能的參考貨幣:

package com.quartetfs.pivot.sandbox.postprocessor.impl; 

import java.util.ArrayList; 
import java.util.Collection; 
import java.util.Collections; 
import java.util.List; 
import java.util.Properties; 
import java.util.Set; 

import com.quartetfs.biz.pivot.cube.hierarchy.axis.impl.AAnalysisDimension; 
import com.quartetfs.fwk.QuartetExtendedPluginValue; 

/** 
* 
* An analysis dimension bearing the 
* list of possible reference currencies. 
* 
* @author Quartet FS 
* 
*/ 
@QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.cube.hierarchy.IDimension", key = ReferenceCurrencyDimension.TYPE) 
public class ReferenceCurrencyDimension extends AAnalysisDimension { 

    /** serialVersionUID */ 
    private static final long serialVersionUID = 42706811331081328L; 

    /** Default reference currency */ 
    public static final String DEFAULT_CURRENCY = "EUR"; 

    /** Static list of non-default possible reference currencies */ 
    public static final List<Object[]> CURRENCIES; 
    static { 
     List<Object[]> currencies = new ArrayList<Object[]>(); 
     currencies.add(new Object[] {"USD"}); 
     currencies.add(new Object[] {"GBP"}); 
     currencies.add(new Object[] {"JPY"}); 
     CURRENCIES = Collections.unmodifiableList(currencies); 
    } 

    /** Plugin type */ 
    public static final String TYPE = "REF_CCY"; 

    /** Constructor */ 
    public ReferenceCurrencyDimension(String name, int ordinal, Properties properties, Set<String> measureGroups) { 
     super(name, ordinal, properties, measureGroups); 
    } 

    @Override 
    public Object getDefaultDiscriminator(int levelOrdinal) { return DEFAULT_CURRENCY; } 

    @Override 
    public Collection<Object[]> buildDiscriminatorPaths() { return CURRENCIES; } 

    @Override 
    public int getLevelsCount() { return 1; } 

    @Override 
    public String getLevelName(int levelOrdinal) { 
     return levelOrdinal == 0 ? "Currency" : super.getLevelName(levelOrdinal); 
    } 

    @Override 
    public String getType() { return TYPE; } 

} 

然後,後處理器本身,定製的動態聚合後處理器修改,以處理的分析維數和輸出相同的骨料多次,每參考貨幣一次。

package com.quartetfs.pivot.sandbox.postprocessor.impl; 

import java.util.List; 
import java.util.Properties; 

import com.quartetfs.biz.pivot.IActivePivot; 
import com.quartetfs.biz.pivot.ILocation; 
import com.quartetfs.biz.pivot.ILocationPattern; 
import com.quartetfs.biz.pivot.aggfun.IAggregationFunction; 
import com.quartetfs.biz.pivot.cellset.ICellSet; 
import com.quartetfs.biz.pivot.cube.hierarchy.IDimension; 
import com.quartetfs.biz.pivot.cube.hierarchy.axis.IAxisMember; 
import com.quartetfs.biz.pivot.impl.Location; 
import com.quartetfs.biz.pivot.postprocessing.impl.ADynamicAggregationPostProcessor; 
import com.quartetfs.biz.pivot.postprocessing.impl.ADynamicAggregationProcedure; 
import com.quartetfs.biz.pivot.query.IQueryCache; 
import com.quartetfs.biz.pivot.query.aggregates.IAggregatesRetriever; 
import com.quartetfs.biz.pivot.query.aggregates.RetrievalException; 
import com.quartetfs.fwk.QuartetException; 
import com.quartetfs.fwk.QuartetExtendedPluginValue; 
import com.quartetfs.pivot.sandbox.service.impl.ForexService; 
import com.quartetfs.tech.type.IDataType; 
import com.quartetfs.tech.type.impl.DoubleDataType; 

/** 
* Forex post processor with two features: 
* <ul> 
* <li>Dynamically aggregates amounts in their native currencies into reference currency 
* <li>Applies several reference currencies, exploded along an analysis dimension. 
* </ul> 
* 
* @author Quartet FS 
*/ 
@QuartetExtendedPluginValue(interfaceName = "com.quartetfs.biz.pivot.postprocessing.IPostProcessor", key = ForexPostProcessor.TYPE) 
public class ForexPostProcessor extends ADynamicAggregationPostProcessor<Double> { 

    /** serialVersionUID */ 
    private static final long serialVersionUID = 15874126988574L; 

    /** post processor plugin type */ 
    public final static String TYPE = "FOREX"; 

    /** Post processor return type */ 
    private static final IDataType<Double> DATA_TYPE = new DoubleDataType(); 

    /** Ordinal of the native currency dimension */ 
    protected int nativeCurrencyDimensionOrdinal; 

    /** Ordinal of the native currency level */ 
    protected int nativeCurrencyLevelOrdinal; 

    /** Ordinal of the reference currencies dimension */ 
    protected int referenceCurrenciesOrdinal; 

    /** forex service*/ 
    private ForexService forexService; 

    /** constructor */ 
    public ForexPostProcessor(String name, IActivePivot pivot) { 
     super(name, pivot); 
    } 

    /** Don't forget to inject the Forex service into the post processor */ 
    public void setForexService(ForexService forexService) { 
     this.forexService = forexService; 
    } 

    /** post processor initialization */ 
    @Override 
    public void init(Properties properties) throws QuartetException { 
     super.init(properties); 

     nativeCurrencyDimensionOrdinal = leafLevelsOrdinals.get(0)[0]; 
     nativeCurrencyLevelOrdinal = leafLevelsOrdinals.get(0)[1]; 

     IDimension referenceCurrenciesDimension = getDimension("ReferenceCurrencies"); 
     referenceCurrenciesOrdinal = referenceCurrenciesDimension.getOrdinal(); 
    } 

    /** 
    * Handling of the analysis dimension:<br> 
    * Before retrieving leaves, wildcard the reference currencies dimension. 
    */ 
    protected ICellSet retrieveLeaves(ILocation location, IAggregatesRetriever retriever) throws RetrievalException { 
     ILocation baseLocation = location; 
     if(location.getLevelDepth(referenceCurrenciesOrdinal-1) > 0) { 
      Object[][] array = location.arrayCopy(); 
      array[referenceCurrenciesOrdinal-1][0] = null; // wildcard 
      baseLocation = new Location(array); 
     } 
     return super.retrieveLeaves(baseLocation, retriever); 
    } 



    /** 
    * Perform the evaluation of the post processor on a leaf (as defined in the properties). 
    * Here the leaf level is the UnderlierCurrency level in the Underlyings dimension . 
    */ 
    @Override 
    protected Double doLeafEvaluation(ILocation leafLocation, Object[] underlyingMeasures) throws QuartetException { 

     // Extract the native and reference currencies from the evaluated location 
     String currency = (String) leafLocation.getCoordinate(nativeCurrencyDimensionOrdinal-1, nativeCurrencyLevelOrdinal); 
     String refCurrency = (String) leafLocation.getCoordinate(referenceCurrenciesOrdinal-1, 0); 

     // Retrieve the measure in the native currency 
     double nativeAmount = (Double) underlyingMeasures[0]; 

     // If currency is reference currency or measureNative is equal to 0.0 no need to convert 
     if ((currency.equals(refCurrency)) || (nativeAmount == .0)) return nativeAmount; 

     // Retrieve the rate and rely on the IQueryCache 
     // in order to retrieve the same rate for the same currency for our query 
     IQueryCache queryCache = pivot.getContext().get(IQueryCache.class); 
     Double rate = (Double) queryCache.get(currency + "_" + refCurrency); 
     if(rate == null) { 
      Double rateRetrieved = forexService.retrieveQuotation(currency, refCurrency); 
      Double rateCached = (Double) queryCache.putIfAbsent(currency + "_" + refCurrency, rateRetrieved); 
      rate = rateCached == null ? rateRetrieved : rateCached; 
     } 

     // Compute equivalent in reference currency 
     return rate == null ? nativeAmount : nativeAmount * rate; 
    } 

    @Override 
    protected IDataType<Double> getDataType() { return DATA_TYPE; } 

    /** @return the type of this post processor, within the post processor extended plugin. */ 
    @Override 
    public String getType() { return TYPE; } 


    /** 
    * @return our own custom dynamic aggregation procedure, 
    * so that we can inject our business logic. 
    */ 
    protected DynamicAggregationProcedure createProcedure(ICellSet cellSet, IAggregationFunction aggregationFunction, ILocationPattern pattern) { 
     return new DynamicAggregationProcedure(cellSet, aggregationFunction, pattern); 
    } 

    /** 
    * Custom dynamic aggregation procedure.<br> 
    * When the procedure is executed over a leaf location, 
    * we produce several aggregates instead of only one: 
    * one aggregate for each of the visible reference currencies. 
    */ 
    protected class DynamicAggregationProcedure extends ADynamicAggregationProcedure<Double> { 

     protected DynamicAggregationProcedure(ICellSet cellSet, IAggregationFunction aggregationFunction, ILocationPattern pattern) { 
      super(ForexPostProcessor.this, aggregationFunction, cellSet, pattern); 
     } 

     /** 
     * Execute the procedure over one row of the leaf cell set. 
     * We compute one aggregate for each of the reference currencies. 
     */ 
     @Override 
     public boolean execute(ILocation location, int rowId, Object[] measures) { 
      if(location.getLevelDepth(referenceCurrenciesOrdinal-1) > 0) { 

       // Lookup the visible reference currencies 
       IDimension referenceCurrenciesDimension = pivot.getDimensions().get(referenceCurrenciesOrdinal); 
       List<IAxisMember> referenceCurrencies = (List<IAxisMember>) referenceCurrenciesDimension.retrieveMembers(0); 
       for(IAxisMember member : referenceCurrencies) { 
        Object[][] array = location.arrayCopy(); 
        array[referenceCurrenciesOrdinal-1][0] = member.getDiscriminator(); 
        ILocation loc = new Location(array); 
        super.execute(loc, rowId, measures); 
       } 
       return true; 
      } else { 
       return super.execute(location, rowId, measures); 
      } 
     } 

     @Override 
     protected Double doLeafEvaluation(ILocation location, Object[] measures) throws QuartetException { 
      return ForexPostProcessor.this.doLeafEvaluation(location, measures); 
     } 

    } 

} 

在您的多維數據集的描述,分析維度和後處理器會暴露這樣的:

... 
<dimension name="ReferenceCurrencies" pluginKey="REF_CCY" /> 
... 
<measure name="cross" isIntrospectionMeasure="false"> 
    <postProcessor pluginKey="FOREX"> 
     <properties> 
      <entry key="id" value="pv.SUM" /> 
      <entry key="underlyingMeasures" value="pv.SUM" /> 
      <entry key="leafLevels" value="[email protected]" /> 
     </properties> 
    </postProcessor> 
</measure> 
... 
+0

非常感謝Antoine,這就是我一直在尋找的東西。 – Hamid

0

分析尺寸帶來了如此多的複雜性,他們應該考慮的其他功能分開你的魔方。處理您的問題的一種方法是:

  1. 添加沿分析維度正確展開的第一個度量。就你而言,它將簡單地沿着ReferenceCurrency複製基礎度量,可選地進行FX轉換。這項措施可以作爲幾項措施的基礎。

  2. 根據通常的動態聚合添加第二個度量。第二個實現非常簡單,因爲它不知道有分析維度。