一、实验内容
单表关联这个实例要求从给出的数据中寻找出所关心的数据,它是对原始数据包含信息的挖掘。
二、实例描述
实例中给出child-parent表,要求输出grandchild-grandparent表。
样例的输入:
child parent
Tom Luck
Tom Jack
Jone Luck
Jone Jack
Luck Mary
Luck Ben
Jack Alice
Jack Jesse
Terry Alice
Terry Jesse
Philip Terry
Philip Alma
Mark Terry
Mark Alma
三、设计思路
分析这个实例,显然需要进行单表连接,连接的是左表的parent列和右表的child列,且左表和右表是同一个表。连接结果中除去连接的两列就是所需要的结果-----grandchild---grandparent表。
要用MapReduce实现这个实例,首先要考虑如何实现表的自连接,其次就是连接列的设置,最后是结果的整理。考虑到MapReduce的shuffle过程会将相同的key值放在一起,所以可以将Map结果的key值设置成待连接的列,然后列中相同的值自然就会连接在一起了。再与最开始的分析联系起来:要连接的是左表的parent列和右表的child列,且左表和右表是同一个表,所以在Map阶段将读入数据分割成child和parent之后,会将parent设置为key,child设置为value进行输出,作为左表。在将一对child和parent中的child设置为key,parent设置成value进行输出,作为右表。
为了区分输出中的左右表,需要在输出的value中再加上左右表信息,比如在value的String最开始处加上字符1表示左表,字符2表示右表。这样在Map的结果中就形成了左表和右表,然后在shuffle过程中完成连接。在Reduce接收到的连接结果中,每个key的value-list就包含了grandchild和grandparent关系。取出每个key的value-list进行解析,将左表中的child放入一个数组,右表中的parent放入一个数组,然后对两个数组求笛卡尔积就是最后的结果。
思路分析:
四、程序代码
package HadoopShiZhang2;
import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
/**
* 程序以调试成功
* 单表关联实例中给出 child-parent(孩子——父母)表,要求输出 grandchild-grandparent(孙子——爷奶)表。
*
*
*/
public classSTjoin {
public static int time = 0;
static final String INPUT_PATH = "hdfs://chaoren:9000/stjoin.txt";
static final String OUT_PATH = "hdfs://chaoren:9000/out";
public static void main(String[] args) throws Exception {
Configurationconf = newConfiguration();
final FileSystem fileSystem =FileSystem.get(new URI(INPUT_PATH),conf);
final Path outPath = new Path(OUT_PATH);
if(fileSystem.exists(outPath)){
fileSystem.delete(outPath,true);
}
final Job job = new Job(conf , STjoin.class.getSimpleName());
//1.1指定读取的文件位于哪里
FileInputFormat.setInputPaths(job,INPUT_PATH);
//指定如何对输入文件进行格式化,把输入文件每一行解析成键值对
job.setInputFormatClass(TextInputFormat.class);
//1.2 指定自定义的map类
job.setMapperClass(MyMapper.class);
//map输出的<k,v>类型。如果<k3,v3>的类型与<k2,v2>类型一致,则可以省略
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//1.3 分区
//job.setPartitionerClass(HashPartitioner.class);
//有一个reduce任务运行
job.setNumReduceTasks(1);
//1.4 TODO排序、分组
//1.5 TODO规约
//2.2 指定自定义reduce类
job.setReducerClass(MyReducer.class);
//指定reduce的输出类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//2.3 指定写出到哪里
FileOutputFormat.setOutputPath(job,outPath);
//指定输出文件的格式化类
job.setOutputFormatClass(TextOutputFormat.class);
//把job提交给JobTracker运行
job.waitForCompletion(true);
}
/*
* map将输入分割child和parent,然后正序输出一次作为右表,反序输出一次作为左表,需要注意的是在输出的value中必须加上左右表的区别标识。
*/
static class MyMapper extends Mapper<LongWritable,Text, Text, Text>{
protected void map(LongWritable key,Text value,Context context)
throws IOException,InterruptedException { // 实现map函数
Stringchildname = newString(); // 孩子名称
Stringparentname = newString(); // 父母名称
Stringrelationtype = newString(); // 左右表标识
//String line = value.toString();
// 输入的一行预处理文本
StringTokenizeritr = newStringTokenizer(value.toString());
String[] values = new String[2];
int i = 0;
while(itr.hasMoreTokens()){
values[i]=itr.nextToken();
i++;
}
//while(line.charAt(i) !=' '){
//i++;}
//String[] values = {line.substring(0,i),line.substring(i+1)};
if(values[0].compareTo("child")!=0){
childname= values[0];
parentname= values[1];
//输出左表
relationtype= "1";//左右表区分标志
context.write(new Text(values[1]), new Text(relationtype+"+"+childname+"+"+parentname));
// 输出右表
relationtype= "2";
context.write(new Text(values[0]), new Text(relationtype+"+"+childname+"+"+parentname));
}
}
}
static class MyReducer extends Reducer<Text, Text,Text, Text>{
protected void reduce(Text key,Iterable<Text> values,Context context)
throws IOException,InterruptedException { // 实现reduce函数
//输出表头
if(time == 0){
context.write(new Text("grandchild"),newText("grandparent"));
time++;
}
int grandchildnum = 0;
Stringgrandchild[] = newString[10];
int grandparentnum = 0;
Stringgrandparent[] = newString[10];
Iteratorite = values.iterator();
while(ite.hasNext()){
Stringrecord = ite.next().toString();
int len = record.length();
int i = 2;
if(len == 0) continue;
// 取得左右表标识
char relationtype =record.charAt(0);
// 定义孩子和父母变量
Stringchildname = newString();
Stringparentname = newString();
//获取value-list中value的child
while(record.charAt(i)!='+'){
childname= childname + record.charAt(i);
i++;
}
i=i+1;
//获取value-list中value的parent
while(i<len){
parentname= parentname + record.charAt(i);
i++;
}
//左边,取出child放入grandchild
if(relationtype == '1'){
grandchild[grandchildnum]= childname;
grandchildnum++;
}
//右表,取出parent放入grandparent
if(relationtype == '2'){
grandparent[grandparentnum]= parentname;
grandparentnum++;
}
}
//grandchild和grandparent数组求笛卡尔积
if(grandparentnum!=0&& grandchildnum!=0){
for(int m =0 ;m <grandchildnum; m++){
for(int n=0;n<grandparentnum;n++){
// 输出结果
context.write(new Text(grandchild[m]), new Text(grandparent[n]));//输出结果
}
}
}
}
}
}
五、实验结果