私のmapreduceジョブを使用した後、これは出力です:
User16565 Logins: 1 Orders:1
User16566 Logins: 2 Orders:2
User16567 Logins: 1 Orders:1
すべてが素晴らしく見えますが、ログファイルに何千ものエントリがある場合、それはあまり役に立ちません。「ログイン」と「注文」を合計して差額を計算できるようにコードを変更する方法はありますか?
編集:新しい質問/問題
ログの例:
2013-01-01T08:48:09.009+0100,feature:login,-,User73511,-,-,-,-
2013-01-01T03:58:05.005+0100,feature:order-created,-,User73511,-,-,-,-
2013-01-01T01:26:30.030+0100,feature:login,-,User14253,-,-,-,-
2013-01-01T19:45:01.001+0100,feature:order-created,-,User73511,-,-,-,-
コードにエラーが見つかりました。ログインと注文が正しくカウントされていないことに気づきました。最初は出力が正しいように見えましたが、ログインと注文を手動で確認したところ、エラーがあることに気付きました。出力:
User73511 Logins: 3 Orders:2
User14253 Logins: 1 Orders:1
する必要があります:
User73511 Logins: 1 Orders:2
User14253 Logins: 1 Orders:0
コード全体は次のとおりです。
public class UserOrderCount {
public static class SingleUserMapper extends
Mapper<LongWritable, Text, Text, CountInformationTuple> {
private Text outUserId = new Text();
private CountInformationTuple outCountOrder = new CountInformationTuple();
@Override
public void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String tempString = value.toString();
String[] singleUserData = tempString.split(",");
String userId = singleUserData[3];
String featureId = singleUserData[1];
if (featureId.contains("feature:order-created")) {
outCountOrder.setCountOrder(1);
}
if (featureId.contains("feature:login")) {
outCountOrder.setCountLogin(1);
}
outUserId.set(userId);
context.write(outUserId, outCountOrder);
}
}
public static class SingleUserReducer extends
Reducer<Text, CountInformationTuple, Text, CountInformationTuple> {
private CountInformationTuple result = new CountInformationTuple();
public void reduce(Text key, Iterable<CountInformationTuple> values,
Context context) throws IOException, InterruptedException {
int login = 0;
int order = 0;
for (CountInformationTuple val : values) {
login += val.getCountLogin();
order += val.getCountOrder();
}
result.setCountLogin(login);
result.setCountOrder(order);
context.write(key, result);
}
}
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
String[] otherArgs = new GenericOptionsParser(conf, args)
.getRemainingArgs();
if (otherArgs.length != 2) {
System.err.println("Usage: UserOrderCount <in> <out>");
System.exit(2);
}
Job job = new Job(conf);
job.setJobName("UserOrderCount");
job.setJarByClass(UserOrderCount.class);
job.setMapperClass(SingleUserMapper.class);
job.setCombinerClass(SingleUserReducer.class);
job.setReducerClass(SingleUserReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(CountInformationTuple.class);
FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
public static class CountInformationTuple implements Writable {
private int countOrder = 0;
private int countLogin = 0;
public int getCountOrder() {
return countOrder;
}
public void setCountOrder(int order) {
this.countOrder = order;
}
public int getCountLogin() {
return countLogin;
}
public void setCountLogin(int login) {
this.countLogin = login;
}
@Override
public void readFields(DataInput in) throws IOException {
countOrder = in.readInt();
countLogin = in.readInt();
}
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(countLogin);
out.writeInt(countOrder);
}
@Override
public String toString() {
return "Logins: "+ countLogin + "\t" + "Orders:" + countOrder;
}
}
}