2015-09-18 16 views
5

Mam do czynienia z dziennikami serwera, które są w formacie JSON, i chcę przechowywać moje dzienniki na AWS S3 w formacie Parkiet (i Parkiet wymaga schematu Avro). Po pierwsze, wszystkie dzienniki mają wspólny zestaw pól, po drugie, wszystkie dzienniki mają wiele opcjonalnych pól, których nie ma we wspólnym zestawie.Jak połączyć rekord z mapą w Avro?

Na przykład follwoing trzy dzienniki:

{ "ip": "172.18.80.109", "timestamp": "2015-09-17T23:00:18.313Z", "message":"blahblahblah"} 
{ "ip": "172.18.80.112", "timestamp": "2015-09-17T23:00:08.297Z", "message":"blahblahblah", "microseconds": 223} 
{ "ip": "172.18.80.113", "timestamp": "2015-09-17T23:00:08.299Z", "message":"blahblahblah", "thread":"http-apr-8080-exec-1147"} 

wszystkich trzech dzienników mają 3 wspólnych pól: ip, timestamp i message niektóre dzienniki mają dodatkowe pola, takie jak microseconds i thread.

Gdybym użyć następującego schematu wtedy stracę wszystkie dodatkowe dziedzinach .:

{"namespace": "example.avro", 
"type": "record", 
"name": "Log", 
"fields": [ 
    {"name": "ip", "type": "string"}, 
    {"name": "timestamp", "type": "String"}, 
    {"name": "message", "type": "string"} 
] 
} 

i następujący schemat działa dobrze:

{"namespace": "example.avro", 
"type": "record", 
"name": "Log", 
"fields": [ 
    {"name": "ip", "type": "string"}, 
    {"name": "timestamp", "type": "String"}, 
    {"name": "message", "type": "string"}, 
    {"name": "microseconds", "type": [null,long]}, 
    {"name": "thread", "type": [null,string]} 
] 
} 

Ale jedynym problemem jest to, że nie znać wszystkie nazwy pól opcjonalnych, chyba że zeskanuję wszystkie logi, poza tym pojawią się nowe dodatkowe pola w przyszłości.

Potem wymyślić pomysł, który łączy record i map:

{"namespace": "example.avro", 
"type": "record", 
"name": "Log", 
"fields": [ 
    {"name": "ip", "type": "string"}, 
    {"name": "timestamp", "type": "String"}, 
    {"name": "message", "type": "string"}, 
    {"type": "map", "values": "string"} // error 
] 
} 

Niestety nie będzie to skompilować:

java -jar avro-tools-1.7.7.jar compile schema example.avro . 

To będzie wyrzucać błąd:

Exception in thread "main" org.apache.avro.SchemaParseException: No field name: {"type":"map","values":"long"} 
    at org.apache.avro.Schema.getRequiredText(Schema.java:1305) 
    at org.apache.avro.Schema.parse(Schema.java:1192) 
    at org.apache.avro.Schema$Parser.parse(Schema.java:965) 
    at org.apache.avro.Schema$Parser.parse(Schema.java:932) 
    at org.apache.avro.tool.SpecificCompilerTool.run(SpecificCompilerTool.java:73) 
    at org.apache.avro.tool.Main.run(Main.java:84) 
    at org.apache.avro.tool.Main.main(Main.java:73) 

Czy istnieje sposób na przechowywanie ciągów JSON w formacie Avro, które są flexib le radzić sobie z nieznanymi opcjonalnymi polami?

Zasadniczo jest to problem z ewolucją schematu , Spark może poradzić sobie z tym problemem przez Schema Merging. Szukam rozwiązania z Hadoop.

+0

Twoja mapa nie ma atrybutu nazwy. Daj to. :) – oakad

+0

Chyba nigdy nie próbujesz avro. To nie zadziała. '{" namespace ":" example.avro ", " type ":" record ", " name ":" Log ", " fields ": [ {" name ":" ip "," type ": "string"}, {"name": "timestamp", "type": "string"}, {"name": "message", "type": "string"}, {"name": " addtional "," type ":" map "," values ​​":" string "} ] }' – soulmachine

Odpowiedz

5

Typ mapy jest typem "złożonym" w terminologii avro. Poniższy urywek działa:

{"namespace": "example.avro", 
"type": "record", 
"name": "Log", 
"fields": [ 
    {"name": "ip", "type": "string"}, 
    {"name": "timestamp", "type": "string"}, 
    {"name": "message", "type": "string"}, 
    {"name": "additional", "type": {"type": "map", "values": "string"}} 
    ] 
} 
+0

Dzięki! Ten schemat przejdzie kompilację. Schemat ten umieszcza wszystkie opcjonalne pola w polu 'addtional', np.' {"Ip": "172.18.80.109", "znacznik czasu": "2015-09-17T23: 00: 18.313Z", "message": "bla blash "," addtional ": {" mikrosekund ":" 123 "," wątek ":" http-apr-8080-exec-1147 "}}', ale chcę, aby wszystkie pola opcjonalne były na tym samym poziomie wspólnych pól, jak trzy przykładowe dzienniki w moim pytaniu. – soulmachine

+0

Rekord w avro jest zdefiniowany jako obiekt o stałej liczbie wstępnie zdefiniowanych pól. Możesz też umieścić swoją mapę jako obiekt najwyższego poziomu i traktować wszystkie swoje pola jako klucze do tej mapy. – oakad

+0

Jeśli używam 'map' jako typu najwyższego poziomu, np., '{" type ":" map "," values ​​":" string "}', wtedy wszystkie pola muszą być typu 'string', jeśli istnieją różne typy pól, wtedy' map' jest bezradne. – soulmachine