2012-09-27 6 views
3

내가 말하는 값, 통화가 포함 된 사실로 ActivePivot 큐브가 있다고 가정 해 봅시다. 내 큐브에 Currency가 일반 차원으로 있다고 가정 할 수 있습니다.ActivePivot 리프 수준 집계 및 분석 차원

많은 통화가있는 사실로 큐브를 채 웁니다.

환율을 얻으려면 통화 및 참조 통화를 사용하는 외환 서비스가 있습니다.

이제 Value.SUM은 이해가되지 않습니다. 다른 통화로 값을 합산하기 때문에 모든 값을 참조 통화 (예 : USD)로 변환 할 수있는 후 처리기를 갖기를 원합니다. 그래서 우리는 ADynamicAggregationPostProcessor을 확장하는 포스트 프로세서를 작성하고 잎 수준 차원으로 통화를 지정하고 forex 서비스를 사용하여 변환합니다.

하지만 USD로 변환하지 않으려는 경우 10 가지 통화로 변환하고 화면에서 서로 옆에 결과를 표시하려고합니다. 그래서 우리는 10 명의 구성원이있는 ReferenceCurrency라고하는 Analysis 차원을 만듭니다.

내 질문은 : 어떻게 분석 차원을 처리하기 위해 위의 후 처리기를 변경할 수 있습니까? 일반 바닐 ADynamicAggregationPostProcessor은 Analysis 차원을 처리하지 못하며 기본 구성원 만이이 후 처리기에서 볼 수 있습니다. 처럼 DefaultAggregatePostProcessor과 같은 Analysis 차원을 처리하는 다른 포스트 프로세서에는 리프 수준을 지정하는 수단이 없으므로 Currency별로 집계를 가져올 수 없으므로 forex 변환을 수행 할 수 없습니다. 어떻게하면 케이크를 먹을 수 있을까요?

답변

1

ActivePivot의 두 가지 고급 기능 (분석 집계가 동일한 집계의 여러 결과를 표시하고 동적 집계가 다른 통화로 표시된 집계 금액)을 사용하려는 것처럼 보입니다.

각각 별도로 설정 및 몇 줄의 코드를 삽입하여 설정하기가 쉽습니다. 그러나 둘을 인터리빙하려면 사후 프로세서 평가의 내부를 이해하고 비즈니스 로직을 올바른 위치에 주입해야합니다.

다음은 ActivePivot 4.3.3을 기반으로 한 예입니다. 자신의 프로젝트에 적용하기 전에 신속하게 실행할 수 있도록 오픈 소스 샌드 박스 애플리케이션에 작성되었습니다.

먼저 우리는 가능한 기준 통화를 유지하기위한 간단한 분석 측정을 필요

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. 일반 동적 집계를 기반으로 두 번째 측정 값을 추가하십시오. 이 두 번째 구현은 분석 차원이 있는지 모르기 때문에 매우 간단합니다.