fr.inria.oak.paxquery.translation.Logical2Pact.java Source code

Java tutorial

Introduction

Here is the source code for fr.inria.oak.paxquery.translation.Logical2Pact.java

Source

/*******************************************************************************
 * Copyright (C) 2013, 2014, 2015 by Inria and Paris-Sud University
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package fr.inria.oak.paxquery.translation;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import org.apache.commons.lang.SerializationUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.flink.api.common.Plan;
import org.apache.flink.api.common.operators.Operator;
import org.apache.flink.api.java.record.operators.CoGroupOperator;
import org.apache.flink.api.java.record.operators.CrossOperator;
import org.apache.flink.api.java.record.operators.FileDataSink;
import org.apache.flink.api.java.record.operators.FileDataSource;
import org.apache.flink.api.java.record.operators.JoinOperator;
import org.apache.flink.api.java.record.operators.MapOperator;
import org.apache.flink.api.java.record.operators.ReduceOperator;
import org.apache.flink.types.IntValue;
import org.apache.flink.types.Record;
import org.apache.flink.types.StringValue;

import fr.inria.oak.paxquery.algebra.logicalplan.LogicalPlan;
import fr.inria.oak.paxquery.algebra.operators.BaseLogicalOperator;
import fr.inria.oak.paxquery.algebra.operators.binary.CartesianProduct;
import fr.inria.oak.paxquery.algebra.operators.binary.Join;
import fr.inria.oak.paxquery.algebra.operators.binary.LeftOuterJoin;
import fr.inria.oak.paxquery.algebra.operators.binary.LeftOuterNestedJoin;
import fr.inria.oak.paxquery.algebra.operators.binary.LeftOuterNestedJoinWithAggregation;
import fr.inria.oak.paxquery.algebra.operators.border.XMLConstruct;
import fr.inria.oak.paxquery.algebra.operators.border.XMLScan;
import fr.inria.oak.paxquery.algebra.operators.border.XMLTreeConstruct;
import fr.inria.oak.paxquery.algebra.operators.unary.Aggregation;
import fr.inria.oak.paxquery.algebra.operators.unary.DuplicateElimination;
import fr.inria.oak.paxquery.algebra.operators.unary.Flatten;
import fr.inria.oak.paxquery.algebra.operators.unary.GroupBy;
import fr.inria.oak.paxquery.algebra.operators.unary.GroupByWithAggregation;
import fr.inria.oak.paxquery.algebra.operators.unary.Navigation;
import fr.inria.oak.paxquery.algebra.operators.unary.Projection;
import fr.inria.oak.paxquery.algebra.operators.unary.Selection;
import fr.inria.oak.paxquery.common.datamodel.metadata.MetadataTypes;
import fr.inria.oak.paxquery.common.datamodel.metadata.NestedMetadata;
import fr.inria.oak.paxquery.common.datamodel.metadata.NestedMetadataUtils;
import fr.inria.oak.paxquery.common.exception.PAXQueryExecutionException;
import fr.inria.oak.paxquery.common.predicates.DisjunctivePredicate;
import fr.inria.oak.paxquery.common.xml.navigation.NavigationTreePatternUtils;
import fr.inria.oak.paxquery.pact.configuration.PACTOperatorsConfiguration;
import fr.inria.oak.paxquery.pact.datamodel.metadata.MetadataTypesMapping;
import fr.inria.oak.paxquery.pact.io.XmlConsTreePatternOutputFormat;
import fr.inria.oak.paxquery.pact.io.XmlNavTreePatternInputFormat;
import fr.inria.oak.paxquery.pact.io.XmlOutputFormat;
import fr.inria.oak.paxquery.pact.operations.KeyFactoryOperations;
import fr.inria.oak.paxquery.pact.operators.binary.CartesianProductOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ConjEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ConjLNOEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ConjLNOEquiJoinWithAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ConjLOEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.DisjEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.DisjLNOEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.DisjLNOEquiJoinWithAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.binary.DisjLOEquiJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ThetaJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ThetaLNOJoinOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ThetaLNOJoinWithAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.binary.ThetaLOJoinOperator;
import fr.inria.oak.paxquery.pact.operators.unary.DuplicateEliminationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.FlattenOperator;
import fr.inria.oak.paxquery.pact.operators.unary.GroupByOperator;
import fr.inria.oak.paxquery.pact.operators.unary.GroupByWithAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.NavigationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.NestedAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.PostAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.PostLNOJoinOperator;
import fr.inria.oak.paxquery.pact.operators.unary.PostLNOJoinWithAggregationOperator;
import fr.inria.oak.paxquery.pact.operators.unary.PostLOJoinOperator;
import fr.inria.oak.paxquery.pact.operators.unary.ProjectionOperator;
import fr.inria.oak.paxquery.pact.operators.unary.SelectionOperator;

/**
 * Translation of a logical expression into a PACT plan.
 * 
 */
public class Logical2Pact {

    private static final Log logger = LogFactory.getLog(Logical2Pact.class);

    public static final Plan planTranslate(LogicalPlan logPlan) {
        BaseLogicalOperator log = logPlan.getRoot();

        logger.debug("After pushing: " + log.getName());

        FileDataSink result;
        if (log instanceof XMLConstruct)
            result = planTranslate((XMLConstruct) log);
        else if (log instanceof XMLTreeConstruct)
            result = planTranslate((XMLTreeConstruct) log);
        else
            throw new PAXQueryExecutionException("The top operator must translate into a data sink!");

        Plan resultPactPlan = new Plan(result);
        return resultPactPlan;
    }

    private static final FileDataSink planTranslate(XMLConstruct as) {
        //Generate plan
        Operator<Record>[] childPlan = translate(as.getChild());

        //Store translation in a list
        List<Operator<Record>> children = new ArrayList<Operator<Record>>();
        for (int i = 0; i < childPlan.length; i++)
            children.add(childPlan[i]);

        //Create FileDataSink
        FileDataSink result = new FileDataSink(XmlOutputFormat.class, as.getOutputPath(), children,
                "Construct XML");
        XmlOutputFormat.configureRecordFormat(result).setSignature(as.getNRSMD()).setApply(as.getApply());
        //      result.setDegreeOfParallelism(1);

        return result;
    }

    private static final FileDataSink planTranslate(XMLTreeConstruct as) {
        //Generate plan
        Operator<Record>[] childPlan = translate(as.getChild());

        //Store translation in a list
        List<Operator<Record>> children = new ArrayList<Operator<Record>>();
        for (int i = 0; i < childPlan.length; i++)
            children.add(childPlan[i]);

        //Create FileDataSink
        FileDataSink result = new FileDataSink(XmlConsTreePatternOutputFormat.class, as.getOutputPath(), children,
                "Construct XML");
        XmlConsTreePatternOutputFormat.configureRecordFormat(result).setSignature(as.getNRSMD())
                .setConstructionTreePattern(as.getConstructionTreePattern());
        //      result.setDegreeOfParallelism(1);

        return result;
    }

    private static final Operator<Record>[] translate(BaseLogicalOperator log) {
        Operator<Record>[] translation = null;

        if (log instanceof XMLScan)
            translation = translate((XMLScan) log);
        else if (log instanceof Selection)
            translation = translate((Selection) log);
        else if (log instanceof Projection)
            translation = translate((Projection) log);
        else if (log instanceof Navigation)
            translation = translate((Navigation) log);
        else if (log instanceof GroupBy)
            translation = translate((GroupBy) log);
        else if (log instanceof Flatten)
            translation = translate((Flatten) log);
        else if (log instanceof Aggregation)
            translation = translate((Aggregation) log);
        else if (log instanceof DuplicateElimination)
            translation = translate((DuplicateElimination) log);
        else if (log instanceof CartesianProduct)
            translation = translate((CartesianProduct) log);
        else if (log instanceof Join)
            translation = translate((Join) log);
        else if (log instanceof LeftOuterJoin)
            translation = translate((LeftOuterJoin) log);
        else if (log instanceof LeftOuterNestedJoin)
            translation = translate((LeftOuterNestedJoin) log);
        else
            throw new PAXQueryExecutionException("Translation not implemented for operator " + log.getName());

        return translation;
    }

    private static final Operator<Record>[] translate(XMLScan xp) {
        FileDataSource navigationExtraction = new FileDataSource(XmlNavTreePatternInputFormat.class,
                xp.getPathDocuments(), "Parse XML");
        if (xp.getNavigationTreePattern() != null)
            XmlNavTreePatternInputFormat.configureXmlNavInputFormat(navigationExtraction)
                    .setNavigationTreePattern(xp.getNavigationTreePattern())
                    .setAttachDocumentID(xp.isAttachDocumentID());
        else
            XmlNavTreePatternInputFormat.configureXmlNavInputFormat(navigationExtraction)
                    .setAttachDocumentID(xp.isAttachDocumentID());
        return new Operator[] { navigationExtraction };
    }

    private static final Operator<Record>[] translate(Selection sel) {
        Operator<Record>[] childPlan = translate(sel.getChild());

        // create MapOperator for selecting some records
        MapOperator selection = MapOperator.builder(SelectionOperator.class).input(childPlan).name("Select")
                .build();

        // selection configuration
        final String encodedNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(sel.getNRSMD()));
        selection.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
        final String encodedPredicate = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(sel.getPred()));
        selection.setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);

        return new Operator[] { selection };
    }

    private static final Operator<Record>[] translate(Projection proj) {
        Operator<Record>[] childPlan = translate(proj.getChild());

        // create MapOperator for projecting a column
        MapOperator projection = MapOperator.builder(ProjectionOperator.class).input(childPlan).name("Proj")
                .build();

        // projection configuration
        final String encodedNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(proj.getNRSMD()));
        projection.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
        final String encodedKeepColumns = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(proj.columns));
        projection.setParameter(PACTOperatorsConfiguration.KEEP_COLUMNS_BINARY.toString(), encodedKeepColumns);

        return new Operator[] { projection };
    }

    private static final Operator<Record>[] translate(Navigation nav) {
        Operator<Record>[] childPlan = translate(nav.getChild());

        // create MapOperator for navigating in a column
        MapOperator navigation = MapOperator.builder(NavigationOperator.class).input(childPlan).name("TPNav")
                .build();

        // navigation configuration
        final String encodedNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(nav.getNRSMD()));
        navigation.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
        navigation.setParameter(PACTOperatorsConfiguration.NAVIGATION_COLUMN_INT.toString(), nav.pos);
        navigation.setParameter(PACTOperatorsConfiguration.NTP_STRING.toString(),
                NavigationTreePatternUtils.getParsableStringFromTreePattern(nav.navigationTreePattern));

        return new Operator[] { navigation };
    }

    private static final Operator<Record>[] translate(Flatten flat) {
        Operator<Record>[] childPlan = translate(flat.getChild());

        // create MapOperator to flatten tuples
        MapOperator flatten = MapOperator.builder(FlattenOperator.class).input(childPlan).name("Flatten").build();

        // flatten configuration
        final String encodedFlattenNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(flat.getNRSMD()));
        flatten.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedFlattenNRSMD);
        final String encodedUnnestPath = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(flat.getUnnestPath()));
        flatten.setParameter(PACTOperatorsConfiguration.UNNEST_PATH_BINARY.toString(), encodedUnnestPath);

        return new Operator[] { flatten };
    }

    private static final Operator<Record>[] translate(GroupBy gb) {
        final boolean withAggregation = gb instanceof GroupByWithAggregation;

        Operator<Record>[] childPlan = translate(gb.getChild());

        // create ReduceOperator for grouping
        ReduceOperator.Builder groupByBuilder;
        if (withAggregation)
            groupByBuilder = ReduceOperator.builder(GroupByWithAggregationOperator.class).input(childPlan)
                    .name("GroupByAgg");
        else
            groupByBuilder = ReduceOperator.builder(GroupByOperator.class).input(childPlan).name("GroupBy");
        for (int column : gb.getReduceByColumns())
            KeyFactoryOperations.addKey(groupByBuilder,
                    MetadataTypesMapping.getKeyClass(gb.getChild().getNRSMD().getType(column)), column);
        ReduceOperator groupBy = groupByBuilder.build();

        // groupBy configuration
        final String encodedNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(gb.getNRSMD()));
        groupBy.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
        final String encodedGroupByColumns = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize((gb.getGroupByColumns())));
        groupBy.setParameter(PACTOperatorsConfiguration.GROUP_BY_COLUMNS_BINARY.toString(), encodedGroupByColumns);
        final String encodedNestColumns = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize((gb.getNestColumns())));
        groupBy.setParameter(PACTOperatorsConfiguration.NEST_COLUMNS_BINARY.toString(), encodedNestColumns);
        if (withAggregation) {
            GroupByWithAggregation gba = (GroupByWithAggregation) gb;

            groupBy.setParameter(PACTOperatorsConfiguration.AGGREGATION_COLUMN_INT.toString(),
                    gba.getAggregationColumn());

            final String encodedAggregationType = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(gba.getAggregationType()));
            groupBy.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                    encodedAggregationType);

            groupBy.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                    gba.isExcludeNestedField());
        }

        return new Operator[] { groupBy };
    }

    private static final Operator<Record>[] translate(DuplicateElimination dupElim) {
        Operator<Record>[] childPlan = translate(dupElim.getChild());

        // create ReduceOperator for removing records
        ReduceOperator.Builder duplicateEliminationBuilder = ReduceOperator
                .builder(DuplicateEliminationOperator.class).input(childPlan).name("DupElim");
        for (int column : dupElim.getColumns())
            KeyFactoryOperations.addKey(duplicateEliminationBuilder,
                    MetadataTypesMapping.getKeyClass(dupElim.getChild().getNRSMD().getType(column)), column);
        ReduceOperator duplicateElimination = duplicateEliminationBuilder.build();

        // projection configuration
        final String encodedNRSMD = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(dupElim.getNRSMD()));
        duplicateElimination.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
        final String encodedDuplicateEliminationColumns = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(dupElim.getColumns()));
        duplicateElimination.setParameter(PACTOperatorsConfiguration.DUP_ELIM_COLUMNS_BINARY.toString(),
                encodedDuplicateEliminationColumns);

        return new Operator[] { duplicateElimination };
    }

    private static final Operator<Record>[] translate(Aggregation aggr) {
        Operator<Record>[] childPlan = translate(aggr.getChild());

        Operator<Record> aggregation;
        if (aggr.getAggregationPath().length > 1) {
            // create MapOperator for aggregating
            aggregation = MapOperator.builder(NestedAggregationOperator.class).input(childPlan).name("Aggr")
                    .build();

            // aggregation configuration
            final String encodedNRSMD = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(aggr.getNRSMD()));
            aggregation.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD);
            final String aggregationPath = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(aggr.getAggregationPath()));
            aggregation.setParameter(PACTOperatorsConfiguration.AGGREGATION_PATH_BINARY.toString(),
                    aggregationPath);
            final String aggregationType = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(aggr.getAggregationType()));
            aggregation.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                    aggregationType);
        } else {
            //First, we create the NRSMD of the (pre) groupBy
            NestedMetadata groupByNRSMD = aggr.getNRSMD();
            MetadataTypes[] attScanMeta = new MetadataTypes[1];
            attScanMeta[0] = MetadataTypes.INTEGER_TYPE;
            final NestedMetadata auxColumnNRSMD = new NestedMetadata(1, attScanMeta);
            groupByNRSMD = NestedMetadataUtils.appendNRSMD(groupByNRSMD, auxColumnNRSMD);

            //Then, we create ReduceOperator for grouping using the document ID column
            ReduceOperator.Builder groupByBuilder = ReduceOperator.builder(GroupByWithAggregationOperator.class)
                    .input(childPlan).name("GroupByAgg");
            KeyFactoryOperations.addKey(groupByBuilder, StringValue.class, aggr.getDocumentIDColumn());
            ReduceOperator groupBy = groupByBuilder.build();

            // groupBy configuration
            final String encodedNRSMDGroupBy = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(groupByNRSMD));
            groupBy.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMDGroupBy);

            final String encodedGroupByColumns = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(new int[] {}));
            groupBy.setParameter(PACTOperatorsConfiguration.GROUP_BY_COLUMNS_BINARY.toString(),
                    encodedGroupByColumns);

            final NestedMetadata childNRSMD = aggr.getChild().getNRSMD();
            int[] nestColumns = new int[childNRSMD.getColNo()];
            for (int i = 0; i < childNRSMD.getColNo(); i++)
                nestColumns[i] = i;
            final String encodedNestColumns = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(nestColumns));
            groupBy.setParameter(PACTOperatorsConfiguration.NEST_COLUMNS_BINARY.toString(), encodedNestColumns);

            groupBy.setParameter(PACTOperatorsConfiguration.AGGREGATION_COLUMN_INT.toString(),
                    aggr.getAggregationPath()[0]);

            final String encodedAggregationType = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(aggr.getAggregationType()));
            groupBy.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                    encodedAggregationType);

            groupBy.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                    aggr.isExcludeNestedField());

            groupBy.setParameter(PACTOperatorsConfiguration.ATTACH_DUMMY_COLUMN_BOOLEAN.toString(), true);

            // create ReduceOperator for aggregating
            ReduceOperator.Builder aggregationBuilder = ReduceOperator.builder(PostAggregationOperator.class)
                    .input(groupBy).name("PostAggr");
            KeyFactoryOperations.addKey(aggregationBuilder, IntValue.class, groupByNRSMD.colNo - 1);
            aggregation = aggregationBuilder.build();

            //Post-aggregation configuration
            final String encodedNRSMDPostAggregation = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(aggr.getNRSMD()));
            aggregation.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(),
                    encodedNRSMDPostAggregation);
            if (aggr.isExcludeNestedField())
                aggregation.setParameter(PACTOperatorsConfiguration.POST_AGGREGATION_COLUMN_INT.toString(), 0);
            else {
                aggregation.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(), 0);
                aggregation.setParameter(PACTOperatorsConfiguration.POST_AGGREGATION_COLUMN_INT.toString(), 1);
            }

            aggregation.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                    encodedAggregationType);

            aggregation.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                    aggr.isExcludeNestedField());
        }

        return new Operator[] { aggregation };
    }

    private static final Operator<Record>[] translate(CartesianProduct cp) {
        Operator<Record>[] childPlan1 = translate(cp.getLeft());
        Operator<Record>[] childPlan2 = translate(cp.getRight());

        // create CrossOperator for cartesian product
        CrossOperator cartesianProduct = CrossOperator.builder(CartesianProductOperator.class).input1(childPlan1)
                .input2(childPlan2).name("Product").build();

        // cartesian product configuration
        final String encodedNRSMD1 = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(cp.getLeft().getNRSMD()));
        cartesianProduct.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
        final String encodedNRSMD2 = DatatypeConverter
                .printBase64Binary(SerializationUtils.serialize(cp.getRight().getNRSMD()));
        cartesianProduct.setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);

        return new Operator[] { cartesianProduct };
    }

    private static final Operator<Record>[] translate(Join j) {
        Operator<Record>[] childPlan1 = translate(j.getLeft());
        Operator<Record>[] childPlan2 = translate(j.getRight());

        Operator<Record>[] join;

        if (!j.getPred().isOnlyEqui()) { //THETA INNER JOIN
            // create CrossOperator for theta join
            join = new Operator[] { CrossOperator.builder(ThetaJoinOperator.class).input1(childPlan1)
                    .input2(childPlan2).name("JoinEval").build() };

            //  for theta join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getLeft().getNRSMD()));
            join[0].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getRight().getNRSMD()));
            join[0].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getPred()));
            join[0].setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);
        } else if (j.getPred() instanceof DisjunctivePredicate
                && ((DisjunctivePredicate) j.getPred()).getConjunctivePreds().size() != 1) { //DISJ INNER JOIN
            // create CoGroup contracts for disjunctive equi join
            DisjunctivePredicate disjPred = (DisjunctivePredicate) j.getPred();
            final int[][] leftColumns = j.getPred().getLeftColumns();
            final int[][] rightColumns = j.getPred().getRightColumns();

            //Parameters that will be used later for configuring each contract
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getLeft().getNRSMD()));
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getRight().getNRSMD()));
            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(disjPred));

            final NestedMetadata leftNRSMD = j.getLeft().getNRSMD();

            //We create the join with the number of contracts needed for evaluation
            join = new Operator[disjPred.getConjunctivePreds().size()];
            for (int i = 0; i < join.length; i++) {
                JoinOperator.Builder conjEquiJoinBuilder = JoinOperator.builder(DisjEquiJoinOperator.class,
                        MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[i][0])), leftColumns[i][0],
                        rightColumns[i][0] - leftNRSMD.getColNo());
                conjEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("JoinEval(" + i + ")");

                for (int k = 1; k < leftColumns[i].length; k++)
                    KeyFactoryOperations.addKey(conjEquiJoinBuilder,
                            MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[i][k])),
                            leftColumns[i][k], rightColumns[i][k] - leftNRSMD.getColNo());

                join[i] = conjEquiJoinBuilder.build();
                // join configuration
                join[i].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
                join[i].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
                join[i].setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);
                join[i].setParameter(PACTOperatorsConfiguration.PRED_INT.toString(), i);
            }
        } else { //CONJ INNER JOIN
            // create JoinOperator for conjunctive equi join
            final int[] leftColumns = j.getPred().getLeftColumns()[0];
            final int[] rightColumns = j.getPred().getRightColumns()[0];

            final NestedMetadata leftNRSMD = j.getLeft().getNRSMD();

            JoinOperator.Builder conjEquiJoinBuilder = JoinOperator.builder(ConjEquiJoinOperator.class,
                    MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[0])), leftColumns[0],
                    rightColumns[0] - leftNRSMD.getColNo());
            conjEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("JoinConcat");

            for (int i = 1; i < leftColumns.length; i++)
                KeyFactoryOperations.addKey(conjEquiJoinBuilder,
                        MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[i])), leftColumns[i],
                        rightColumns[i] - leftNRSMD.getColNo());

            join = new Operator[] { conjEquiJoinBuilder.build() };
            // for equi join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getLeft().getNRSMD()));
            join[0].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(j.getRight().getNRSMD()));
            join[0].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
        }

        return join;
    }

    private static final Operator<Record>[] translate(LeftOuterJoin loj) {
        Operator<Record>[] childPlan1 = translate(loj.getLeft());
        Operator<Record>[] childPlan2 = translate(loj.getRight());

        Operator<Record>[] conjLeftOuterJoin;

        if (!loj.getPred().isOnlyEqui()) { // THETA
            // 1) create CrossOperator for join processing
            CrossOperator thetaJoin = CrossOperator.builder(ThetaLOJoinOperator.class).input1(childPlan1)
                    .input2(childPlan2).name("LOJoinEval").build();

            // theta join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getLeft().getNRSMD()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getRight().getNRSMD()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);

            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getPred()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);

            // 2) create ReduceOperator for post join processing
            ReduceOperator.Builder postJoinBuilder = ReduceOperator.builder(PostLOJoinOperator.class)
                    .input(thetaJoin).name("PostLOJoin");
            //Document ID column
            KeyFactoryOperations.addKey(postJoinBuilder,
                    MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(loj.getDocumentIDColumn())),
                    loj.getDocumentIDColumn());
            //Node ID columns
            for (int index : loj.getNodeIDColumns()) {
                KeyFactoryOperations.addKey(postJoinBuilder,
                        MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(index)), index);
            }
            ReduceOperator postJoin = postJoinBuilder.build();

            // postJoin configuration
            // we create the definitive NRSMD
            final NestedMetadata nrsmdPostJoin = loj.getNRSMD();
            final String encodedNRSMDPostJoin = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(nrsmdPostJoin));
            postJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMDPostJoin);
            postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                    loj.getLeft().getNRSMD().getColNo());
            postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                    loj.getNRSMD().getColNo());

            conjLeftOuterJoin = new Operator[] { postJoin };
        } else if (loj.getPred() instanceof DisjunctivePredicate
                && ((DisjunctivePredicate) loj.getPred()).getConjunctivePreds().size() != 1) { // DISJ EQUI                  
            // 1) create CoGroup contracts for join processing
            // create CoGroup contracts for disjunctive equi join
            DisjunctivePredicate disjPred = (DisjunctivePredicate) loj.getPred();
            final int[][] leftColumns = loj.getPred().getLeftColumns();
            final int[][] rightColumns = loj.getPred().getRightColumns();

            //Parameters that will be used later for configuring each contract         
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getLeft().getNRSMD()));
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getRight().getNRSMD()));
            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(disjPred));

            //We create the join with the number of contracts needed for evaluation
            Operator[] disjJoin = new Operator[disjPred.getConjunctivePreds().size()];
            for (int i = 0; i < disjJoin.length; i++) {
                CoGroupOperator.Builder conjEquiJoinBuilder = CoGroupOperator.builder(DisjLOEquiJoinOperator.class,
                        MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(leftColumns[i][0])),
                        leftColumns[i][0], rightColumns[i][0] - loj.getLeft().getNRSMD().getColNo());
                conjEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("LOJoinEval(" + i + ")");

                for (int k = 1; k < leftColumns[i].length; k++)
                    KeyFactoryOperations.addKey(conjEquiJoinBuilder,
                            MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(leftColumns[i][k])),
                            leftColumns[i][k], rightColumns[i][k] - loj.getLeft().getNRSMD().getColNo());

                disjJoin[i] = conjEquiJoinBuilder.build();
                // join configuration
                disjJoin[i].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.PRED_INT.toString(), i);
            }

            // 2) create ReduceOperator for post join processing
            ReduceOperator.Builder postJoinBuilder = ReduceOperator.builder(PostLOJoinOperator.class)
                    .input(disjJoin).name("PostLOJoin");
            //Document ID column
            KeyFactoryOperations.addKey(postJoinBuilder,
                    MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(loj.getDocumentIDColumn())),
                    loj.getDocumentIDColumn());
            //Node ID columns
            for (int index : loj.getNodeIDColumns()) {
                KeyFactoryOperations.addKey(postJoinBuilder,
                        MetadataTypesMapping.getKeyClass(loj.getLeft().getNRSMD().getType(index)), index);
            }
            ReduceOperator postJoin = postJoinBuilder.build();

            // postJoin configuration
            // we create the definitive NRSMD
            final NestedMetadata nrsmdPostJoin = loj.getNRSMD();
            final String encodedNRSMDPostJoin = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(nrsmdPostJoin));
            postJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMDPostJoin);
            postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                    loj.getLeft().getNRSMD().getColNo());
            postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                    loj.getNRSMD().getColNo());

            conjLeftOuterJoin = new Operator[] { postJoin };
        } else { // CONJ EQUI
            // create JoinOperator for conjunctive equi join
            final int[] leftColumns = loj.getPred().getLeftColumns()[0];
            final int[] rightColumns = loj.getPred().getRightColumns()[0];

            final NestedMetadata leftNRSMD = loj.getLeft().getNRSMD();

            CoGroupOperator.Builder conjLeftOuterEquiJoinBuilder = CoGroupOperator.builder(
                    ConjLOEquiJoinOperator.class,
                    MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[0])), leftColumns[0],
                    rightColumns[0] - leftNRSMD.getColNo());
            conjLeftOuterEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("LOJoinConcat");

            for (int i = 1; i < leftColumns.length; i++)
                KeyFactoryOperations.addKey(conjLeftOuterEquiJoinBuilder,
                        MetadataTypesMapping.getKeyClass(leftNRSMD.getType(leftColumns[i])), leftColumns[i],
                        rightColumns[i] - leftNRSMD.getColNo());

            conjLeftOuterJoin = new Operator[] { conjLeftOuterEquiJoinBuilder.build() };
            // for equi join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getLeft().getNRSMD()));
            conjLeftOuterJoin[0].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(loj.getRight().getNRSMD()));
            conjLeftOuterJoin[0].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
        }

        return conjLeftOuterJoin;
    }

    private static final Operator<Record>[] translate(LeftOuterNestedJoin lonj) {
        final boolean withAggregation = lonj instanceof LeftOuterNestedJoinWithAggregation;

        Operator<Record>[] childPlan1 = translate(lonj.getLeft());
        Operator<Record>[] childPlan2 = translate(lonj.getRight());

        Operator<Record>[] conjLeftOuterNestedJoin;

        if (!lonj.getPred().isOnlyEqui()) { // THETA
            // 1) create CrossOperator for join processing
            CrossOperator thetaJoin;
            if (withAggregation)
                thetaJoin = CrossOperator.builder(ThetaLNOJoinWithAggregationOperator.class).input1(childPlan1)
                        .input2(childPlan2).name("LNOJoinEvalAgg").build();
            else
                thetaJoin = CrossOperator.builder(ThetaLNOJoinOperator.class).input1(childPlan1).input2(childPlan2)
                        .name("LNOJoinEval").build();

            // theta join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getLeft().getNRSMD()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getRight().getNRSMD()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getPred()));
            thetaJoin.setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);
            if (withAggregation) {
                LeftOuterNestedJoinWithAggregation lonja = (LeftOuterNestedJoinWithAggregation) lonj;

                thetaJoin.setParameter(PACTOperatorsConfiguration.AGGREGATION_COLUMN_INT.toString(),
                        lonja.getAggregationColumn() - lonja.getLeft().getNRSMD().getColNo());

                final String encodedAggregationType = DatatypeConverter
                        .printBase64Binary(SerializationUtils.serialize(lonja.getAggregationType()));
                thetaJoin.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                        encodedAggregationType);

                thetaJoin.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                        lonja.isExcludeNestedField());
            }

            // 2) create ReduceOperator for post join processing
            ReduceOperator.Builder postJoinBuilder;
            if (withAggregation)
                postJoinBuilder = ReduceOperator.builder(PostLNOJoinWithAggregationOperator.class).input(thetaJoin)
                        .name("PostLNOJoinAgg");
            else
                postJoinBuilder = ReduceOperator.builder(PostLNOJoinOperator.class).input(thetaJoin)
                        .name("PostLNOJoin");
            //Document ID column
            KeyFactoryOperations.addKey(postJoinBuilder,
                    MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(lonj.getDocumentIDColumn())),
                    lonj.getDocumentIDColumn());
            //Node ID columns
            for (int index : lonj.getNodeIDColumns()) {
                KeyFactoryOperations.addKey(postJoinBuilder,
                        MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(index)), index);
            }
            ReduceOperator postJoin = postJoinBuilder.build();

            // postJoin configuration
            // we create the definitive NRSMD
            final NestedMetadata nrsmdPostJoin = lonj.getNRSMD();
            final String encodedNRSMDPostJoin = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(nrsmdPostJoin));
            postJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMDPostJoin);
            if (withAggregation) {
                LeftOuterNestedJoinWithAggregation lonja = (LeftOuterNestedJoinWithAggregation) lonj;

                if (lonja.isExcludeNestedField())
                    postJoin.setParameter(PACTOperatorsConfiguration.COMBINATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo());
                else {
                    postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo());
                    postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo() + 1);
                    postJoin.setParameter(PACTOperatorsConfiguration.COMBINATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo() + 2);
                }

                final String encodedAggregationType = DatatypeConverter
                        .printBase64Binary(SerializationUtils.serialize(lonja.getAggregationType()));
                postJoin.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                        encodedAggregationType);

                postJoin.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                        lonja.isExcludeNestedField());
            } else {
                postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                        lonj.getLeft().getNRSMD().getColNo());
                postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                        lonj.getLeft().getNRSMD().getColNo() + 1);
            }

            conjLeftOuterNestedJoin = new Operator[] { postJoin };
        } else if (lonj.getPred() instanceof DisjunctivePredicate
                && ((DisjunctivePredicate) lonj.getPred()).getConjunctivePreds().size() != 1) { // DISJ EQUI                  
            // 1) create CoGroup contracts for join processing
            // create CoGroup contracts for disjunctive equi join
            DisjunctivePredicate disjPred = (DisjunctivePredicate) lonj.getPred();
            final int[][] leftColumns = lonj.getPred().getLeftColumns();
            final int[][] rightColumns = lonj.getPred().getRightColumns();

            //Parameters that will be used later for configuring each contract
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getLeft().getNRSMD()));
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getRight().getNRSMD()));
            final String encodedPredicate = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(disjPred));

            //We create the join with the number of contracts needed for evaluation
            Operator[] disjJoin = new Operator[disjPred.getConjunctivePreds().size()];
            for (int i = 0; i < disjJoin.length; i++) {
                CoGroupOperator.Builder conjEquiJoinBuilder;
                if (withAggregation) {
                    conjEquiJoinBuilder = CoGroupOperator.builder(DisjLNOEquiJoinWithAggregationOperator.class,
                            MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[i][0])),
                            leftColumns[i][0], rightColumns[i][0] - lonj.getLeft().getNRSMD().getColNo());
                    conjEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("LNOJoinEvalAgg(" + i + ")");
                } else {
                    conjEquiJoinBuilder = CoGroupOperator.builder(DisjLNOEquiJoinOperator.class,
                            MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[i][0])),
                            leftColumns[i][0], rightColumns[i][0] - lonj.getLeft().getNRSMD().getColNo());
                    conjEquiJoinBuilder.input1(childPlan1).input2(childPlan2).name("LNOJoinEval(" + i + ")");
                }

                for (int k = 1; k < leftColumns[i].length; k++)
                    KeyFactoryOperations.addKey(conjEquiJoinBuilder,
                            MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[i][k])),
                            leftColumns[i][k], rightColumns[i][k] - lonj.getLeft().getNRSMD().getColNo());

                disjJoin[i] = conjEquiJoinBuilder.build();
                // join configuration
                disjJoin[i].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMD1);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(), encodedNRSMD2);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.PRED_BINARY.toString(), encodedPredicate);
                disjJoin[i].setParameter(PACTOperatorsConfiguration.PRED_INT.toString(), i);
                if (withAggregation) {
                    LeftOuterNestedJoinWithAggregation lonja = (LeftOuterNestedJoinWithAggregation) lonj;

                    disjJoin[i].setParameter(PACTOperatorsConfiguration.AGGREGATION_COLUMN_INT.toString(),
                            lonja.getAggregationColumn() - lonja.getLeft().getNRSMD().getColNo());

                    final String encodedAggregationType = DatatypeConverter
                            .printBase64Binary(SerializationUtils.serialize(lonja.getAggregationType()));
                    disjJoin[i].setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                            encodedAggregationType);

                    disjJoin[i].setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                            lonja.isExcludeNestedField());
                }
            }

            // 2) create ReduceOperator for post join processing
            ReduceOperator.Builder postJoinBuilder;
            if (withAggregation)
                postJoinBuilder = ReduceOperator.builder(PostLNOJoinWithAggregationOperator.class).input(disjJoin)
                        .name("PostLNOJoinAgg");
            else
                postJoinBuilder = ReduceOperator.builder(PostLNOJoinOperator.class).input(disjJoin)
                        .name("PostLNOJoin");

            //Document ID column
            KeyFactoryOperations.addKey(postJoinBuilder,
                    MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(lonj.getDocumentIDColumn())),
                    lonj.getDocumentIDColumn());
            //Node ID column
            for (int index : lonj.getNodeIDColumns()) {
                KeyFactoryOperations.addKey(postJoinBuilder,
                        MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(index)), index);
            }
            ReduceOperator postJoin = postJoinBuilder.build();

            // postJoin configuration
            // we create the definitive NRSMD
            final NestedMetadata nrsmdPostJoin = lonj.getNRSMD();
            final String encodedNRSMDPostJoin = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(nrsmdPostJoin));
            postJoin.setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(), encodedNRSMDPostJoin);
            if (withAggregation) {
                LeftOuterNestedJoinWithAggregation lonja = (LeftOuterNestedJoinWithAggregation) lonj;

                if (lonja.isExcludeNestedField())
                    postJoin.setParameter(PACTOperatorsConfiguration.COMBINATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo());
                else {
                    postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo());
                    postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo() + 1);
                    postJoin.setParameter(PACTOperatorsConfiguration.COMBINATION_COLUMN_INT.toString(),
                            lonj.getLeft().getNRSMD().getColNo() + 2);
                }

                final String encodedAggregationType = DatatypeConverter
                        .printBase64Binary(SerializationUtils.serialize(lonja.getAggregationType()));
                postJoin.setParameter(PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(),
                        encodedAggregationType);

                postJoin.setParameter(PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                        lonja.isExcludeNestedField());
            } else {
                postJoin.setParameter(PACTOperatorsConfiguration.NESTED_RECORDS_COLUMN_INT.toString(),
                        lonj.getLeft().getNRSMD().getColNo());
                postJoin.setParameter(PACTOperatorsConfiguration.EVALUATION_COLUMN_INT.toString(),
                        lonj.getLeft().getNRSMD().getColNo() + 1);
            }

            conjLeftOuterNestedJoin = new Operator[] { postJoin };
        } else { // CONJ EQUI
            // create JoinOperator for conjunctive equi join
            int[] leftColumns = lonj.getPred().getLeftColumns()[0];
            int[] rightColumns = lonj.getPred().getRightColumns()[0];

            CoGroupOperator.Builder conjLeftOuterNestedEquiJoinBuilder;
            if (withAggregation)
                conjLeftOuterNestedEquiJoinBuilder = CoGroupOperator
                        .builder(ConjLNOEquiJoinWithAggregationOperator.class,
                                MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[0])),
                                leftColumns[0], rightColumns[0] - lonj.getLeft().getNRSMD().getColNo())
                        .input1(childPlan1).input2(childPlan2).name("LNOJoinConcatAgg");
            else
                conjLeftOuterNestedEquiJoinBuilder = CoGroupOperator
                        .builder(ConjLNOEquiJoinOperator.class,
                                MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[0])),
                                leftColumns[0], rightColumns[0] - lonj.getLeft().getNRSMD().getColNo())
                        .input1(childPlan1).input2(childPlan2).name("LNOJoinConcat");

            for (int i = 1; i < leftColumns.length; i++)
                KeyFactoryOperations.addKey(conjLeftOuterNestedEquiJoinBuilder,
                        MetadataTypesMapping.getKeyClass(lonj.getLeft().getNRSMD().getType(leftColumns[i])),
                        leftColumns[i], rightColumns[i] - lonj.getLeft().getNRSMD().getColNo());
            conjLeftOuterNestedJoin = new Operator[] { conjLeftOuterNestedEquiJoinBuilder.build() };

            // for equi join configuration
            final String encodedNRSMD1 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getLeft().getNRSMD()));
            conjLeftOuterNestedJoin[0].setParameter(PACTOperatorsConfiguration.NRSMD1_BINARY.toString(),
                    encodedNRSMD1);
            final String encodedNRSMD2 = DatatypeConverter
                    .printBase64Binary(SerializationUtils.serialize(lonj.getRight().getNRSMD()));
            conjLeftOuterNestedJoin[0].setParameter(PACTOperatorsConfiguration.NRSMD2_BINARY.toString(),
                    encodedNRSMD2);
            if (withAggregation) {
                LeftOuterNestedJoinWithAggregation lonja = (LeftOuterNestedJoinWithAggregation) lonj;

                conjLeftOuterNestedJoin[0].setParameter(
                        PACTOperatorsConfiguration.AGGREGATION_COLUMN_INT.toString(),
                        lonja.getAggregationColumn() - lonja.getLeft().getNRSMD().getColNo());

                final String encodedAggregationType = DatatypeConverter
                        .printBase64Binary(SerializationUtils.serialize(lonja.getAggregationType()));
                conjLeftOuterNestedJoin[0].setParameter(
                        PACTOperatorsConfiguration.AGGREGATION_TYPE_BINARY.toString(), encodedAggregationType);

                conjLeftOuterNestedJoin[0].setParameter(
                        PACTOperatorsConfiguration.EXCLUDE_NESTED_FIELD_BOOLEAN.toString(),
                        lonja.isExcludeNestedField());
            }
        }

        return conjLeftOuterNestedJoin;
    }

}