входные данные объединителя-редуктора hadoop-mapreduce

Я изучаю MapReduce, но у меня возникают некоторые проблемы, вот ситуация: у меня есть два файла: «users» содержит список пользователей с некоторыми их данными (пол, возраст, страна и т. д...) файл выглядит так:

user_000003  m  22  United States   Oct 30, 2005

«songs» содержит данные о песнях, прослушанных всеми пользователями (идентификатор пользователя, дата и время прослушивания, идентификатор исполнителя, имя исполнителя, идентификатор песни, название песни):

user_000999 2008-12-11T22:52:33Z    b7ffd2af-418f-4be2-bdd1-22f8b48613da    Nine Inch Nails 1d1bb32a-5bc6-4b6f-88cc-c043f6c52509    7 Ghosts I

цель состоит в том, чтобы найти k самых популярных песен в определенных странах. с k и списком стран, предоставленным на входе.

Я решил использовать класс MultipleInputs для преобразователя, чтобы один преобразователь выдавал набор пар ключ-значение, который выглядит следующим образом: . другой маппер будет выводить . насколько я знаю, я должен иметь возможность читать все значения в паре с определенным ключом (поэтому я должен найти страну и определенное количество песен в списке значений, связанных с идентификатором пользователя) в редюсере и вывести набор файл с парами, которые будут выделены другим заданием MapReduce.

Я почти уверен, что картографы делают свою работу, так как я смог записать их вывод с помощью редуктора.

ОБНОВЛЕНИЕ: файлы передаются мапперам со следующим кодом:

Job job = Job.getInstance(conf);

        MultipleInputs.addInputPath(job, new Path(songsFile), TextInputFormat.class, SongMapper.class);
        MultipleInputs.addInputPath(job, new Path(usersFile), TextInputFormat.class, UserMapper.class);

        FileOutputFormat.setOutputPath(job, new Path(outFile));
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        job.setJarByClass(Songs.class);

        job.setCombinerClass(Combiner.class);
        job.setReducerClass(UserSongReducer.class);

Код картографа:

public static class UserMapper extends Mapper<LongWritable, Text, Text, Text>{

        //empty cleanUp()

        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {

            String record = value.toString();               
            String[] userData = record.split("\t");
            if(userData.length>3 && !userData[3].equals(""))
            {
                context.write(new Text(userData[0]), new Text(userData[3]));
            }        


        }

        //empty run() and setup()

    }

    public static class SongMapper extends Mapper<LongWritable, Text, Text, Text>{

        //empty cleanUp()

        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {

            String record = value.toString();               
            String[] songData = record.split("\t");
            if(songData.length>3 && !songData[3].equals(""))
            {
                context.write(new Text(songData[0]), new Text(songData[5]+" |||  "+songData[3]));
            }        


        }

        //empty run() and setup()

    }

Код комбайна:

public static class Combiner extends Reducer<Text, Text, Text, Text> 
    {

        private boolean isCountryAllowed(String toCheck, String[] countries)
        {

            for(int i=0; i<countries.length;i++)
            {
                if(toCheck.equals(countries[i]))
                    return true;
            }
            return false;
        }

        public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException 
        {



            ArrayList<String> list = new ArrayList<String>();



            String country = "foo";
            for(Text value : values) 
            {
                if(!value.toString().contains(" ||| "))
                {
                    country = value.toString();
                }else
                {
                    list.add(value.toString());
                }

            }

            if(isCountryAllowed(country, context.getConfiguration().getStrings("countries")))
            {

                for (String listVal : list) 
                {
                    context.write(new Text(country), 
                            new Text(listVal));
                }
            }

         }
    }

Проблемы возникают, когда я пытаюсь вывести пары с редуктором:

public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException 
        {


             for (Text value : values) 
            {
                context.write(key,value);
            }
            }

         }

Я использовал « ||| » для создания строки «исполнитель + название», проблема в том, что страна остается «foo». Я думаю, что должен увидеть хотя бы одну строку вывода с правильной нацией в качестве ключа, но вывод всегда "foo" (файл песен 2,5 КБ):

foo Deep Dish |||  Fuck Me Im Famous (Pacha Ibiza)-09-28-2007
foo Vnv Nation |||  Kingdom
foo Les Fleur De Lys |||  Circles
foo Home Video |||  Penguin
foo Of Montreal |||  Will You Come And Fetch Me
foo Godspeed You! Black Emperor |||  Bbf3
foo Alarum |||  Sustained Connection
foo Sneaker Pimps |||  Walking Zero
foo Cecilio And Kapono |||  I Love You
foo Garbage |||  Vow
foo The Brian Setzer Orchestra |||  Gettin' In The Mood
foo Nitin Sawhney |||  Sunset (J-Walk Remix)
foo Nine Inch Nails |||  Heresy
foo Collective Soul |||  Crowded Head
foo Vicarious Bliss |||  Limousine
foo Noisettes |||  Malice In Wonderland
foo Black Rebel Motorcycle Club |||  Lien On Your Dreams
foo Mae |||  Brink Of Disaster
foo Michael Andrews |||  Rosie Darko
foo A Perfect Circle |||  Blue

Что я делаю неправильно?

PS У меня должна быть возможность избежать второго задания, если я использую собственный объединитель, действует ли объединитель точно так же, как редюсер?


person jack_the_beast    schedule 23.05.2014    source источник
comment
сведения о ваших картографах отсутствуют. Пожалуйста, предоставьте их, потому что там уже может быть что-то не так.   -  person DDW    schedule 27.05.2014
comment
Обратите внимание, что, поскольку никогда не гарантируется, что объединитель будет вызван, он не может решить ни одной проблемы.   -  person DDW    schedule 27.05.2014
comment
пожалуйста, проверьте мой обновленный вопрос.   -  person jack_the_beast    schedule 27.05.2014
comment
Было бы полезно увидеть полные конфигурации заданий для двух заданий. Особенно те части, где вы определили картографы. Вы также где-то определяете OutputFormat? Я бы добавил некоторый System.out.println в начале редукторов, чтобы увидеть, какие значения поступают.   -  person woopi    schedule 27.05.2014
comment
@jacopo: я удалил свои ответы, так как не знал о функциональности MutlipleInputs..   -  person DDW    schedule 30.05.2014


Ответы (1)


Из вашего кода я вижу, что страна всегда должна быть «foo». Чего вы именно пытаетесь достичь?

 // IN YOUR MAPPER THE VALUE IS WRITTEN USING |||

context.write(new Text(songData[0]), new Text(songData[5]+" |||  "+songData[3]));

// THEN VALUE WILL ALWAYS CONTAIN ||| AND WILL NEVER CHANGE THE COUNTRY THAT WAS SET TO TRUE
 String country = "foo";
     if(!value.toString().contains(" ||| ")) //never --- 

И затем вы выводите переменную страны:

 context.write(new Text(country),
person griffon vulture    schedule 01.06.2014