2013-01-18 126 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站點的輿論。