/** * Copyright [2011] [Datasalt Systems S.L.] * * 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 com.datasalt.utils.mapred.crossproduct; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.BytesWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.OutputFormat; import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat; import com.datasalt.utils.mapred.crossproduct.io.CrossProductExtraKey; import com.datasalt.utils.mapred.crossproduct.io.CrossProductPair; import com.datasalt.utils.mapred.joiner.MultiJoinChanneledMapper; import com.datasalt.utils.mapred.joiner.MultiJoinDatum; import com.datasalt.utils.mapred.joiner.MultiJoinPair; import com.datasalt.utils.mapred.joiner.MultiJoinPairText; import com.datasalt.utils.mapred.joiner.MultiJoinReducer; import com.datasalt.utils.mapred.joiner.MultiJoiner; /** * This Job is an optional step that is applied depending on the output of {@link CrossProductMapRed}. See the Cross * Product API for reference. * <p> * This Job shouldn't be instantiated or used directly. * * @author pere */ @SuppressWarnings({ "rawtypes" }) public class BigGroupsCrossProductJob { public static enum Counters { TOTALGROUPS, ROWSEMITTED } public final static byte A = 'A'; public final static byte B = 'B'; public static class BigGroupsMap extends MultiJoinChanneledMapper<CrossProductExtraKey, CrossProductPair, CrossProductPair> { Text secondarySort = new Text(); @Override protected void map(CrossProductExtraKey key, CrossProductPair value, Context context) throws IOException, InterruptedException { if(value.getRight().getLength() > 0) { secondarySort.set(A + ""); } else { secondarySort.set(B + ""); } emit(key, secondarySort, value); }; } public static class BigGroupsRed extends MultiJoinReducer<CrossProductPair, NullWritable> { List<BytesWritable> memoryList = new ArrayList<BytesWritable>(); protected void reduce(MultiJoinPair key, Iterable<MultiJoinDatum<?>> values, Context ctx) throws IOException, InterruptedException { memoryList.clear(); ctx.getCounter("stats", Counters.TOTALGROUPS + "").increment(1); for(MultiJoinDatum bag : values) { /* * * */ CrossProductPair pair = deserialize(bag); if(pair.getRight().getLength() > 0) { BytesWritable newWritable = new BytesWritable(); newWritable.set(pair.getRight()); memoryList.add(newWritable); // TODO maybe not really necessary to copy array } else { /* * Perform cross-product */ for(BytesWritable element : memoryList) { pair.setRight(element); ctx.write(pair, NullWritable.get()); ctx.getCounter("stats", Counters.ROWSEMITTED + "").increment(1); } } } }; } public static Job get(Path inputGlob, String basePath, Class<? extends OutputFormat> outputFormat, String outPath, Configuration conf) throws IOException { MultiJoiner multiJoiner = new MultiJoiner("Big Groups Cross Product", conf); multiJoiner.setReducer(BigGroupsRed.class); multiJoiner.setOutputKeyClass(CrossProductPair.class); multiJoiner.setOutputValueClass(NullWritable.class); multiJoiner.setOutputFormat(outputFormat); multiJoiner.setOutputPath(new Path(outPath)); return multiJoiner .addChanneledInput(0, inputGlob, CrossProductPair.class, SequenceFileInputFormat.class, BigGroupsMap.class) .setMultiJoinPairClass(MultiJoinPairText.class).getJob(); } }