Apache Pig - Как получить количество подходящих элементов между несколькими сумками?

Я новый пользователь Apache Pig и мне нужно решить проблему.

Я пытаюсь сделать небольшую поисковую систему с Apache Pig. Идея проста: у меня есть файл, который представляет собой объединение нескольких документов (по одному документу в строке). Вот пример с тремя документами:

1,word1 word4 word2 word1
2,word2 word6 word1 word5 word3
3,word1 word3 word4 word5

Затем я создаю Мешок слов для каждого документа, используя эти строки кода:

docs = LOAD '$documents' USING PigStorage(',') AS (id:int, line:chararray);
B = FOREACH docs GENERATE line;
C = FOREACH B GENERATE TOKENIZE(line) as gu;

Затем я удаляю повторяющиеся записи на сумках:

filtered = FOREACH C {
    uniq = DISTINCT gu;
    GENERATE uniq;
}

Вот результаты этого кода:

DUMP filtered;

({(word1), (word4),  (word2)})
({(word2), (word6),  (word1), (word5), (word3)})
({(word1), (word3),  (word4), (word5)})

Таким образом, у меня есть пакет слов в документе, как я хотел.

Теперь давайте рассмотрим пользовательский запрос как файл:

word2 word7 word5

Я превращаю запрос в пакет слов:

query = LOAD '$query' AS (line_query:chararray);
bag_query = FOREACH query GENERATE TOKENIZE(line_query) AS quer;

DUMP bag_query;

Вот результаты:

({(word2), (word7), (word5)})

Теперь вот моя проблема: я хотел бы получить количество совпадений между запросом и каждым документом. В этом примере я хотел бы получить такой вывод:

1
2
1

Я пытался соединиться между сумками, но это не сработало.

Не могли бы вы мне помочь, пожалуйста?

Спасибо.

3 ответа

Решение

Если вы можете не использовать ни одну из UDF, это можно сделать, повернув мешки и выполнив весь стиль SQL.

docs = LOAD '/input/search.dat' USING PigStorage(',') AS (id:int, line:chararray);
C = FOREACH docs GENERATE id, TOKENIZE(line) as gu;
pivoted = FOREACH C {
    uniq = DISTINCT gu;
        GENERATE id, FLATTEN(uniq) as word;
};
filtered = FILTER pivoted BY word MATCHES '(word2|word7|word5)';
--dump filtered;
count_id_matched = FOREACH (GROUP filtered BY id) GENERATE group as id, COUNT(filtered) as count;

dump count_id_matched;

count_word_matched_in_docs = FOREACH (GROUP filtered BY word) GENERATE group as word, COUNT(filtered) as count;

dump count_word_matched_in_docs;

Попробуйте использовать SetIntersect (Datafu UDF - https://github.com/linkedin/datafu) и SIZE, чтобы получить количество элементов в наборе результатов.

Как указывал SNeumann, вы можете использовать DataFu SetIntersect для вашего примера.

Основываясь на вашем примере, учитывая эти документы:

1,word1 word4 word2 word1
2,word2 word6 word1 word5 word3 word7
3,word1 word3 word4 word5

И учитывая этот запрос:

word2 word7 word5

Тогда этот код дает вам то, что вы хотите:

define SetIntersect datafu.pig.sets.SetIntersect();

docs = LOAD 'docs' USING PigStorage(',') AS (id:int, line:chararray);
B = FOREACH docs GENERATE id, line;
C = FOREACH B GENERATE id, TOKENIZE(line) as gu;

filtered = FOREACH C {
  uniq = DISTINCT gu;
  GENERATE id, uniq;
}

query = LOAD 'query' AS (line_query:chararray);
bag_query = FOREACH query GENERATE TOKENIZE(line_query) AS query;
-- sort the bag of tokens, since SetIntersect requires it
bag_query = FOREACH bag_query {
  query_sorted = ORDER query BY token;
  GENERATE query_sorted;
}

result = FOREACH filtered {
  -- sort the tokens, since SetIntersect requires it
  tokens_sorted = ORDER uniq BY token;
  GENERATE id, 
           SIZE(SetIntersect(tokens_sorted,bag_query.query_sorted)) as cnt;
}

DUMP result;

Значения для результата:

(1,1)
(2,3)
(3,1)

Вот полностью рабочий пример, который вы можете вставить в модульные тесты DataFu для SetIntersect, расположенные здесь:

/**
register $JAR_PATH

define SetIntersect datafu.pig.sets.SetIntersect();

docs = LOAD 'docs' USING PigStorage(',') AS (id:int, line:chararray);
B = FOREACH docs GENERATE id, line;
C = FOREACH B GENERATE id, TOKENIZE(line) as gu;

filtered = FOREACH C {
  uniq = DISTINCT gu;
  GENERATE id, uniq;
}

query = LOAD 'query' AS (line_query:chararray);
bag_query = FOREACH query GENERATE TOKENIZE(line_query) AS query;
-- sort the bag of tokens, since SetIntersect requires it
bag_query = FOREACH bag_query {
  query_sorted = ORDER query BY token;
  GENERATE query_sorted;
}

result = FOREACH filtered {
  -- sort the tokens, since SetIntersect requires it
  tokens_sorted = ORDER uniq BY token;
  GENERATE id, 
           SIZE(SetIntersect(tokens_sorted,bag_query.query_sorted)) as cnt;
}

DUMP result;

 */
@Multiline
private String setIntersectTestExample;

@Test
public void setIntersectTestExample() throws Exception
{    
  PigTest test = createPigTestFromString(setIntersectTestExample);    

  writeLinesToFile("docs", 
                   "1,word1 word4 word2 word1",
                   "2,word2 word6 word1 word5 word3 word7",
                   "3,word1 word3 word4 word5");

  writeLinesToFile("query", 
                   "word2 word7 word5");

  test.runScript();

  super.getLinesForAlias(test, "filtered");
  super.getLinesForAlias(test, "query");
  super.getLinesForAlias(test, "result");
}

Если у вас есть другие подобные варианты использования, я бы хотел их услышать:) Мы всегда стремимся предоставить более полезные пользовательские функции в DataFu.

Другие вопросы по тегам