3

値と通貨のみを含むファクトを持つActivePivotキューブがあるとします。私の立方体が通常の次元として通貨を持っているとしましょう。

多くの通貨を持つファクトでキューブを埋めます。

レートを取得するために通貨と参照通貨を使用する外国為替サービスがあります。

さて、Value.SUMは意味がありません。異なる通貨で値を合計しているので、すべての値を参照通貨(USDなど)に変換して合計できるポストプロセッサが必要なので、次のように記述します。ADynamicAggregationPostProcessorを拡張し、リーフレベルのディメンションとしてCurrencyを指定し、外国為替サービスを使用して変換を行うポストプロセッサであり、満足しています。

ただし、米ドルだけに変換するのではなく、10の異なる通貨に変換して、画面上で結果を並べて表示したいとします。したがって、10個のメンバーを持つAnalysisディメンション(ReferenceCurrencyなど)を作成します。

私の質問は、分析ディメンションを処理するために上記のポストプロセッサを変更するにはどうすればよいですか?プレーンなバニラADynamicAggregationPostProcessorは分析ディメンションを処理せず、デフォルトのメンバーのみがこのポストプロセッサに表示されます。DefaultAggregatePostProcessorなど、分析ディメンションを処理する他のポストプロセッサには、リーフレベルを指定する手段がないため、通貨で集計を取得できず、外国為替変換も実行できません。どうすればケーキを食べて食べることができますか?

4

2 に答える 2

1

ActivePivotの2つの高度な機能を同時に使用したいようです(同じ集計の複数の結果を公開するための分析ディメンションと、異なる通貨で表された集計金額への動的集計)。

個別に、構成と数行のコードを挿入することで、それぞれをセットアップするのはかなり簡単です。ただし、両方をインターリーブするには、ポストプロセッサ評価の内部を理解し、適切な場所にビジネスロジックを挿入する必要があります。

ActivePivot4.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; }

}

次に、ポストプロセッサ自体、分析ディメンションを処理し、同じ集計を複数回、参照通貨ごとに1回出力するように変更された、カスタマイズされた動的集計ポストプロセッサ。

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="UnderlierCurrency@Underlyings" />
        </properties>
    </postProcessor>
</measure>
...
于 2012-09-28T09:39:33.210 に答える
0

分析ディメンションは非常に複雑であるため、キューブの他の機能とは別に検討する必要があります。問題を処理する1つの方法は次のとおりです。

  1. 分析ディメンションに沿って適切に拡張する最初のメジャーを追加します。あなたの場合、それは単にReferenceCurrencyに沿って基礎となるメジャーをコピーし、オプションでFX変換を実行します。この測定値は、いくつかの測定値の基礎として使用できます。

  2. 通常の動的集計に基づいて、2番目のメジャーを追加します。この2番目の実装は、分析ディメンションがあることを認識していないため、非常に単純です。

于 2012-10-08T15:59:23.020 に答える