2013-01-18 85 views
1

我对猪很新,对日志解析有问题。我目前通过regex_extract在我的url字符串中解析出重要的标签,但我想我应该将整个字符串转换为地图。我正在使用0.10制作一组样本数据,但我开始变得非常迷茫。实际上,我的url字符串有重复的标签。所以我的地图实际上应该是一个以袋子为价值的地图。然后,我可以使用拼合来编写任何后续作业..阿帕奇猪 - url解析成地图

这里是我的测试数据。最后一项显示我的问题与重复标签。

`pig -x local` 
grunt> cat test.log 
test1 user=3553&friend=2042&system=262 
test2 user=12523&friend=26546&browser=firfox 
test2 user=205&friend=3525&friend=353 

我正在使用标记大小来生成内袋。

grunt> A = load 'test.log' as (f:chararray, url:chararray); 
grunt> B = foreach A generate f, TOKENIZE(url,'&') as attr; 
grunt> describe B; 
B: {f: chararray,attr: {tuple_of_tokens: (token: chararray)}} 

grunt> dump B; 
(test1,{(user=3553),(friend=2042),(system=262)}) 
(test2,{(user=12523),(friend=26546),(browser=firfox)}) 
(test2,{(user=205),(friend=3525),(friend=353)}) 

在这些关系中使用嵌套的foreach,但我认为他们有一定的局限性,我不知道..

grunt> C = foreach B { 
>> D = foreach attr generate STRSPLIT($0,'='); 
>> generate f, D as taglist; 
>> } 

grunt> dump C; 
(test1,{((user,3553)),((friend,2042)),((system,262))}) 
(test2,{((user,12523)),((friend,26546)),((browser,firfox))}) 
(test2,{((user,205)),((friend,3525)),((friend,353))}) 

grunt> G = foreach C { 
>> H = foreach taglist generate TOMAP($0.$0, $0.$1) as tagmap; 
>> generate f, H as alltags; 
>> } 

grunt> describe G; 
G: {f: chararray,alltags: {tuple_of_tokens: (tagmap: map[])}} 

grunt> dump G; 
(test1,{([user#3553]),([friend#2042]),([system#262])}) 
(test2,{([user#12523]),([friend#26546]),([browser#firfox])}) 
(test2,{([user#205]),([friend#3525]),([friend#353])}) 

grunt> MAPTEST = foreach G generate f, flatten(alltags.tagmap); 
grunt> describe MAPTEST; 
MAPTEST: {f: chararray,null::tagmap: map[]} 

grunt> res = foreach MAPTEST generate $1#'user'; 
grunt> dump res; 
(3553) 
() 
() 
(12523) 
() 
() 
(205) 
() 
() 

grunt> res = foreach MAPTEST generate $1#'friend'; 
grunt> dump res; 
() 
(2042) 
() 
() 
(26546) 
() 
() 
(3525) 
(353) 

所以,这并不可怕。我认为它很接近,但并不完美。我最大的担忧是我需要将标签分组,至少在我将其添加到地图之前,最后一行有2个“friend”标签。

grunt> dump C; 
(test1,{((user,3553)),((friend,2042)),((system,262))}) 
(test2,{((user,12523)),((friend,26546)),((browser,firfox))}) 
(test2,{((user,205)),((friend,3525)),((friend,353))}) 

我尝试嵌套的foreach与一组,但多数民众赞成在导致错误。

grunt> G = foreach C { 
>> H = foreach taglist generate *; 
>> I = group H by $1; 
>> generate I; 
>> } 
2013-01-18 14:56:31,434 [main] ERROR org.apache.pig.tools.grunt.Grunt - ERROR 1200: <line 34, column 10> Syntax error, unexpected symbol at or near 'H' 

任何人有任何想法如何更接近生成这个URL字符串到地图袋?想象会有一个猪宏或什么的,因为这似乎是一个常见的用例。任何想法都非常感谢。

+0

+1:解释问题的出色工作,展示您尝试过的内容,并提供可简明地展示问题的示例输入。 –

回答

0

好消息和坏消息。好消息是实现这一点非常简单。坏消息是你无法实现我认为理想的东西 - 所有的标签/值对在一张地图中 - 而不诉诸于UDF。

首先,一对夫妇提示:FLATTENSTRSPLIT,这样你就不会在你的元组嵌套的无用的水平,并再次FLATTEN嵌套foreach内的结果,这样你就不需要做以后它。此外,STRSPLIT还有一个可选的第三个参数,以提供最大数量的输出字符串。使用它来保证其输出的模式。这是你的脚本的修改版本:

A = load 'test.log' as (f:chararray, url:chararray); 
B = foreach A generate f, TOKENIZE(url,'&') as attr; 
C = foreach B { 
    D = foreach attr generate FLATTEN(STRSPLIT($0,'=',2)) AS (key:chararray, val:chararray); 
    generate f, FLATTEN(D); 
}; 
E = foreach (group C by (f, key)) generate group.f, TOMAP(group.key, C.val); 
dump E; 

输出:

(test1,[user#{(3553)}]) 
(test1,[friend#{(2042)}]) 
(test1,[system#{(262)}]) 
(test2,[user#{(12523),(205)}]) 
(test2,[friend#{(26546),(3525),(353)}]) 
(test2,[browser#{(firfox)}]) 

你完成拆分出来的标签和值后,group也由标签来获得你的价值观的袋子。然后把它放到地图上。请注意,这假设如果你有两行具有相同的ID(test2,这里),你想要合并它们。如果不是这种情况,则需要为该行构建唯一的标识符。

不幸的是,显然没有办法结合地图而不诉诸UDF,但这应该只是最简单的所有可能的UDF。喜欢的东西(未经测试):

public class COMBINE_MAPS extends EvalFunc<Map> { 
    public Map<String, DataBag> exec(Tuple input) throws IOException { 
     if (input == null || input.size() != 1) { return null; } 

     // Input tuple is a singleton containing the bag of maps 
     DataBag b = (DataBag) input.get(0); 

     // Create map that we will construct and return 
     Map<String, Object> m = new HashMap<String, Object>(); 

     // Iterate through the bag, adding the elements from each map 
     Iterator<Tuple> iter = b.iterator(); 
     while (iter.hasNext()) { 
      Tuple t = iter.next(); 
      m.putAll((Map<String, Object>) t.get(0)); 
     } 

     return m; 
    } 
} 

随着UDF这样的,你可以这样做:

F = foreach (group E by f) generate COMBINE_MAPS(E.$1); 

注意,在这个UDF,如果任何输入地图有重叠的一个会覆盖另一个,并且没有办法提前告诉哪个“赢”。如果这可能是一个问题,你需要添加一些错误检查代码到UDF。

+0

谢谢。这给了我一些工作。对此,我真的非常感激。对我来说,test1和test2并不是真正的关键,它们只是我博客中的其他领域。我只是用它们来更好地表明那是一个内袋。 – jeff

+0

在这种情况下,您可以将整行包含在“group by”字段中,理想情况下该字段是唯一的,然后忽略它。最好的解决方案是编写一个UDF,类似于我在制作地图时显示的那样,没有所有的“组”和“TOMAP”等等。 –

+0

好的。这就说得通了。奇怪它不是由其他人撰写和发表的。看起来像一个很常见的用例。谢谢。 – jeff

0

我想我会更新这个以防万一任何人在将来尝试这样做。我从来没有拉丁拉丁工作,但我走了完整的UDF路线。可悲的是,我不是一个真正的交易程序员,所以java的例子让我失去了一段时间。但是我设法将迄今一直工作的python UDF拼凑在一起。仍然需要清理它,以处理错误,什么不是,但这是现在可用。我确信有更好的java方法来做到这一点。

#!/usr/bin/python 
@outputSchema("tagmap:map[{(value:chararray)}]") 

def inst_url_parse(url_query): 
     query_vals = url_query.split("&") 
     url_query_map = {} 
     for each_val in query_vals: 
       kv = each_val.split("=") 
       if kv[0] in url_query_map: 
         url_query_map[kv[0]].append(kv[1]) 
       else: 
         url_query_map[kv[0]] = [kv[1]] 

     return url_query_map 

我真的很喜欢我们的网址查询存储这种方式,因为每个键可以有0,1,N值。下游作业在eval中称为flatten(tagmap#'key'),与我之前做的相比,它非常无痛。使用这个我们可以更快地开发。我们还hcatalog的数据存储为

querymap<string, array<string>> 

,它似乎使用侧视太蜂巢查询/视图正常工作。谁知道?

对不起,如果这是太过于Q和A站点的舆论。