【问题标题】:apache pig - url parsing into a mapapache pig - 将网址解析为地图
【发布时间】:2013-02-02 10:57:54
【问题描述】:

我对猪很陌生,并且对日志解析有疑问。我目前通过 regex_extract 解析出我的 url 字符串中的重要标签,但我认为我应该将整个字符串转换为地图。我正在使用 0.10 处理一组样本数据,但我开始迷路了。实际上,我的 url 字符串有重复的标签。所以我的地图实际上应该是一张以袋子为值的地图。然后我可以使用 flatten 编写任何后续工作..

这是我的测试数据。最后一个条目显示了我的重复标签问题。

`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 个“朋友”标签,至少在我将其添加到地图之前。

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 字符串生成到袋子地图中?认为会有一个猪宏或其他东西,因为这似乎是一个常见的用例。任何想法都非常感谢。

【问题讨论】:

  • +1:出色地解释了问题,展示了您的尝试,并提供了简洁地展示问题的示例输入。

标签: apache-pig


【解决方案1】:

好消息和坏消息。好消息是实现这一点非常简单。坏消息是,如果不使用 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 添加某种错误检查代码。

【讨论】:

  • 谢谢。这给了我一些可以使用的东西。对此,我真的非常感激。对我来说,test1 和 test2 并不是真正的键,它们只是我博客中的其他字段。我只是用它们来更好地表明这是一个内袋。
  • 在这种情况下,您可以在 group by 字段中包含整行,理想情况下这是唯一的,然后忽略它。实际上,最好的解决方案是编写一个类似于我展示的 UDF 来制作地图,而无需所有 groups 和 TOMAPs 等等。
  • 好的。那讲得通。奇怪的是它不是由其他人编写和发布的。似乎是一个非常常见的用例。谢谢。
【解决方案2】:

我想我会更新这个,以防将来有人尝试这样做。我从来没有让猪拉丁文工作,但我走了完整的 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

我真的很喜欢我们的 URL 查询以这种方式存储,因为每个键可以有 0,1,N 值。下游作业只需在 eval 中调用 flatten(tagmap#'key') ,与我之前所做的相比,它相当轻松。我们可以使用它更快地开发。我们还将数据存储在 hcatalog 中为

querymap<string, array<string>> 

它似乎也适用于使用 LATERAL VIEW 的配置单元查询/视图。谁知道?

对不起,如果这对于问答网站来说过于固执己见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    • 2011-03-12
    • 1970-01-01
    相关资源
    最近更新 更多