Jagan Nalla
07/10/2023, 2:21 PM@udf(input_types=[DataTypes.STRING(), DataTypes.STRING(), DataTypes.STRING(), <http://DataTypes.INT|DataTypes.INT>()],
result_type=DataTypes.MAP(DataTypes.STRING(), DataTypes.STRING())
)
def p_record(first_seen,last_seen,src,rec_count):
record_map ={
"first_seen": first_seen,
"last_seen": last_seen,
"srcid": src,
"rec_count": rec_count
}
return record_map
query ="""select pdns_record(first_seen,last_seen,srcid,rec_count) from src_table"""
ftable = st_env.sql_query(query)
ftable.execute().print()
Error:
bytes_value = value.encode("utf-8")
AttributeError: 'int' object has no attribute 'encode'
at org.apache.beam.runners.fnexecution.control.FnApiControlClient$ResponseStreamObserver.onNext(FnApiControlClient.java:180)
at org.apache.beam.runners.fnexecution.control.FnApiControlClient$ResponseStreamObserver.onNext(FnApiControlClient.java:160)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.stub.ServerCalls$StreamingServerCallHandler$StreamingServerCallListener.onMessage(ServerCalls.java:262)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.ForwardingServerCallListener.onMessage(ForwardingServerCallListener.java:33)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.Contexts$ContextualizedServerCallListener.onMessage(Contexts.java:76)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailableInternal(ServerCallImpl.java:318)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailable(ServerCallImpl.java:301)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1MessagesAvailable.runInContext(ServerImpl.java:834)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at <http://org.apache.beam.vendor.grpc.v1p43p2.io|org.apache.beam.vendor.grpc.v1p43p2.io>.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
... 3 moreJagan Nalla
07/10/2023, 2:22 PMreturn json.dumps(record_map).encode('utf-8') but no help.Eugenio Gastelum
07/10/2023, 8:11 PMst_env.create_temporary_system_function("p_record",p_record)
At leasts that's what this aws repo says https://github.com/aws-samples/pyflink-getting-started/blob/main/pyflink-examples/UDF/udf.py
But it doesn't works for me either. I've posted my errors some comments bellow yours. But let me know if you manage to make it work.
Don't take my word on this cause I am no flink expert, just a random guy trying to use it as wellJagan Nalla
07/11/2023, 12:49 PMcreate table table(
pdns_record MAP(STRING,STRING)
`uuid` STRING,
PRIMARY KEY (uuid) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'property-version' = 'universal',
'topic' = 'pns_topic',
'properties.bootstrap.servers' = 'localhost:5092',
'properties.group.id' = 'pns_toppic_gid',
'key.format' = 'json',
'key.json.ignore-parse-errors' = 'false',
'value.format' = 'json',
'value.json.fail-on-missing-field' = 'false'
)
in this way when we sink to kafka topic the results should look like this {"pdns_record":{"first_seen":"2023-07-10 134314","last_seen":"2023-07-10 134314","rec_count": 2},"uuid":"6c975ce5-fa6c-4a1d-99f3-4222388c25b5"}Dian Fu
07/11/2023, 12:54 PMrecord_map ={
"first_seen": str(first_seen),
"last_seen": str(last_seen),
"srcid": str(src),
"rec_count": str(rec_count)
}Jagan Nalla
07/11/2023, 12:56 PMJagan Nalla
07/11/2023, 12:57 PMDian Fu
07/11/2023, 12:58 PMDataTypes.MAP(DataTypes.STRING(), DataTypes.STRING() and so you must make sure that the key/value of the map must both be of string type. Otherwise, it doesn’t know how to serialize the result.Dian Fu
07/11/2023, 12:59 PMDian Fu
07/11/2023, 1:00 PM@udf(input_types=[DataTypes.STRING(), DataTypes.STRING(), DataTypes.STRING(), <http://DataTypes.INT|DataTypes.INT>()],
result_type=DataTypes.STRING())
)
def p_record(first_seen,last_seen,src,rec_count):
record_map ={
"first_seen": first_seen,
"last_seen": last_seen,
"srcid": src,
"rec_count": rec_count
}
return json.dumps(record_map)Jagan Nalla
07/11/2023, 1:02 PMcreate table table(
pdns_record STRING,
`uuid` STRING,
PRIMARY KEY (uuid) NOT ENFORCED
) WITH (
'connector' = 'upsert-kafka',
'property-version' = 'universal',
'topic' = 'pns_topic',
'properties.bootstrap.servers' = 'localhost:5092',
'properties.group.id' = 'pns_toppic_gid',
'key.format' = 'json',
'key.json.ignore-parse-errors' = 'false',
'value.format' = 'json',
'value.json.fail-on-missing-field' = 'false'
)Jagan Nalla
07/11/2023, 1:03 PMJagan Nalla
07/11/2023, 1:03 PMJagan Nalla
07/11/2023, 1:07 PMJagan Nalla
07/11/2023, 1:08 PMJagan Nalla
07/11/2023, 1:08 PMJagan Nalla
07/11/2023, 2:15 PM{
"schema": {
"type": "struct",
"fields": [
{
"type": "string",
"optional": True,
"field": "first_seen"
},
{
"type": "string",
"optional": True,
"field": "last_seen"
},
{
"type": "string",
"optional": True,
"field": "srcid"
},
{
"type": "int64",
"optional": True,
"field": "rec_count"
},
{
"type": "string",
"optional": True,
"field": "saddr"
},
{
"type": "string",
"optional": True,
"field": "daddr"
},
{
"type": "string",
"optional": True,
"field": "domain"
},
{
"type": "string",
"optional": True,
"field": "response"
},
{
"type": "int64",
"optional": True,
"field": "rtype"
},
{
"type": "string",
"optional": True,
"field": "rtype_str"
},
{
"type": "string",
"optional": True,
"field": "uuid"
}
]
},
"pdns_record":{
"first_seen": "2023-07-11 13:06:07",
"last_seen": "2023-07-11 13:06:07",
"srcid": "abc",
"rec_count": 2,
"saddr": "108.162.245.68",
"daddr": "216.197.73.52",
"domain": "<http://z4686.dsca.akamaiedge.net|z4686.dsca.akamaiedge.net>",
"response": "",
"rtype": 1,
"rtype_str": "A",
"uuid": "c97f2fa1-3e16-4608-a8b8-7df366729140"
}
}Jagan Nalla
07/11/2023, 2:16 PMEugenio Gastelum
07/11/2023, 4:07 PMquery ="""select pdns_record(first_seen,last_seen,srcid,rec_count) from src_table"""
ftable = st_env.sql_query(query)
ftable.execute().print()
It seems you can do something like this
query ="""select JSON_OBJECT(KEY 'first_seen' VALUE first_seen, KEY 'last_seen' VALUE last_seen,KEY 'srcid' VALUE srcid,KEY 'rec_count' VALUE rec_count) as pd_record from src_table"""
ftable = st_env.sql_query(query)
ftable.execute().print()
Based on this article https://aiven.io/blog/preview-JSON-SQL-functions-apache-flink-1.15.0
Which takes from the flink's JSON_OBJECT documentation https://nightlies.apache.org/flink/flink-docs-master/docs/dev/table/functions/systemfunctions/
However, on Flink's documentation they only have examples where the JSON they build is all strings, I didn't saw an example of some fields being string and some integers. Nonetheless it might just be missing that example but it can be possible. At least I searched for the JSON_OBJECT function on other RDS and they do use an example where they mix strings and values like the one the image attached:Eugenio Gastelum
07/11/2023, 4:09 PMEugenio Gastelum
07/11/2023, 4:12 PMJagan Nalla
07/11/2023, 4:27 PMJagan Nalla
07/11/2023, 4:27 PMJagan Nalla
07/11/2023, 4:27 PMJagan Nalla
07/11/2023, 4:28 PMJagan Nalla
07/11/2023, 4:29 PMlike this {"pdns_record": {"first_seen":"2023-07-11 16:23:15","last_seen":"2023-07-11 16:23:15","rec_count":2,"srcid":"mtcv"}}Eugenio Gastelum
07/11/2023, 4:56 PMquery ="""select CAST(JSON_OBJECT(KEY 'first_seen' VALUE first_seen, KEY 'last_seen' VALUE last_seen,KEY 'srcid' VALUE srcid,KEY 'rec_count' VALUE rec_count),STRING) as pd_record from src_table"""
ftable = st_env.sql_query(query)
ftable.execute().print()
If that still introduced backslashes at least we could use some string functions on it to substitute the backslashes for nothingJagan Nalla
07/11/2023, 5:09 PMcast(JSON_OBJECT(KEY 'rec_count' VALUE rec_count, KEY 'first_seen' VALUE first_seen, KEY 'last_seen' VALUE last_seen, KEY 'srcid' VALUE srcid) as string) pdns_recordEugenio Gastelum
07/11/2023, 5:54 PMJagan Nalla
07/11/2023, 6:04 PMJagan Nalla
07/11/2023, 6:09 PMJagan Nalla
07/11/2023, 6:09 PMEugenio Gastelum
07/11/2023, 6:24 PMformat=json for values it casts that column defined as ROW data type into some data type on the JSON output stream. And it casts it into the JSON data type`object` type, which is essentially a JSON.
So I believe if you define your column on the sink table as ROW data type, it might finally get sent as a normal json into the topic.
So maybe try
create table %s (
pdns_record ROW ('rec_count' INTEGER,'first_seen' STRING, 'last_seen' STRING,....<rest_of_fields>)
)
WITH (
.... <same kafka properties>
)Eugenio Gastelum
07/11/2023, 6:30 PMJagan Nalla
07/11/2023, 7:00 PMJagan Nalla
07/11/2023, 7:03 PMEugenio Gastelum
07/11/2023, 7:06 PMSELECT ROW(rec_count,first_seen,last_seen,srcid) FROM cte_flow_pdns_Eugenio Gastelum
07/11/2023, 7:06 PMJagan Nalla
07/11/2023, 7:08 PMEugenio Gastelum
07/11/2023, 7:12 PM@udf(input_types=[DataTypes.STRING(), DataTypes.STRING(), DataTypes.STRING(), <http://DataTypes.INT|DataTypes.INT>()],
result_type=DataTypes.ROW(DataTypes.FIELD('first_seen',DataTypes.STRING()), DataTypes.FIELD('last_seen',DataTypes.STRING()),DataTypes.FIELD('srcid',DataTypes.STRING()),DataTypes.FIELD('rec_count',DataTypes.STRING())
)
def p_record(first_seen,last_seen,src,rec_count):
record_map ={
"first_seen": first_seen,
"last_seen": last_seen,
"srcid": src,
"rec_count": rec_count
}
return record_map
query ="""select pdns_record(first_seen,last_seen,srcid,rec_count) from src_table"""
ftable = st_env.sql_query(query)
ftable.execute().print()Eugenio Gastelum
07/11/2023, 7:12 PMSELECT ROW(rec_count,first_seen,last_seen,srcid) FROM cte_flow_pdns_Jagan Nalla
07/11/2023, 7:40 PM@udf(input_types=[DataTypes.STRING(), DataTypes.STRING(), DataTypes.STRING(), <http://DataTypes.INT|DataTypes.INT>()],
result_type=DataTypes.ROW([DataTypes.FIELD('first_seen',DataTypes.STRING()),DataTypes.FIELD('last_seen',DataTypes.STRING()),DataTypes.FIELD('srcid',DataTypes.STRING()),DataTypes.FIELD('rec_count',DataTypes.STRING())])
)
def pdns_record(first_seen, last_seen, srcid, rec_count):
record_map = {
"first_seen": first_seen,
"last_seen": last_seen,
"srcid": srcid,
"rec_count": rec_count
}
return record_map
Error:
AttributeError: 'dict' object has no attribute 'get_fields_by_names'
at org.apache.beam.runners.fnexecution.control.FnApiControlClient$ResponseStreamObserver.onNext(FnApiControlClient.java:180)
at org.apache.beam.runners.fnexecution.control.FnApiControlClient$ResponseStreamObserver.onNext(FnApiControlClient.java:160)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.stub.ServerCalls$StreamingServerCallHandler$StreamingServerCallListener.onMessage(ServerCalls.java:262)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.ForwardingServerCallListener.onMessage(ForwardingServerCallListener.java:33)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.Contexts$ContextualizedServerCallListener.onMessage(Contexts.java:76)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailableInternal(ServerCallImpl.java:318)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.internal.ServerCallImpl$ServerStreamListenerImpl.messagesAvailable(ServerCallImpl.java:301)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.internal.ServerImpl$JumpToApplicationThreadServerStreamListener$1MessagesAvailable.runInContext(ServerImpl.java:834)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
at org.apache.beam.vendor.grpc.v1p43p2.io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:133)
... 3 moreEugenio Gastelum
07/11/2023, 7:58 PMJSON_OBJECT query for this worked? SELECT ROW(rec_count,first_seen,last_seen,srcid) FROM cte_flow_pdnsJagan Nalla
07/11/2023, 8:08 PMJagan Nalla
07/11/2023, 11:12 PM@udf(result_type=DataTypes.ARRAY(DataTypes.ROW([DataTypes.FIELD("type", DataTypes.STRING()),
DataTypes.FIELD("optional", DataTypes.BOOLEAN()),
DataTypes.FIELD("field", DataTypes.STRING())])))
def json_array_generator():
return [{"type": "string", "optional": True, "field": "first_seen"}]
I defined by sink table with schema to match with the output. Here is code of it.
create table %s(
schema ROW(type STRING, fields ARRAY<ROW(type STRING, optional BOOLEAN, field STRING)>),
pdns_record ROW (rec_count INTEGER,first_seen STRING, last_seen STRING, srcid STRING )
) WITH (
'connector' = 'kafka',
'property-version' = 'universal',
'topic' = '%s',
'properties.bootstrap.servers' = '%s',
'properties.group.id' = '%s',
'format' = 'json'
)
I also registered the udf fucntion
st_env.create_temporary_system_function("json_array_generator", json_array_generator)
When I call the udf function in select query statement I'm getting error saying that
AttributeError: 'dict' object has no attribute 'get_fields_by_names'Jagan Nalla
07/12/2023, 2:10 AM# Define a UDF to generate the JSON array
@udf(result_type= DataTypes.ARRAY(DataTypes.ROW([DataTypes.FIELD("type", DataTypes.STRING()),
DataTypes.FIELD("optional", DataTypes.BOOLEAN()),
DataTypes.FIELD("field", DataTypes.STRING())])))
def schema_json_array_generator():
return [Row("string", True, "first_seen"),
Row("string", True, "last_seen"),
Row("string", True, "srcid"),
Row("int64", True, "rec_count")]
Register the function:
st_env.create_temporary_system_function("schema_json_array_generator", schema_json_array_generator)
Sink Table:
create table %s(
schema ROW(type STRING,fields ARRAY<ROW(type STRING,optional BOOLEAN,field STRING)>),
pdns_record ROW (first_seen STRING,last_seen STRING,srcid STRING,rec_count INTEGER)
) WITH (
'connector' = 'kafka',
'property-version' = 'universal',
'topic' = '%s',
'properties.bootstrap.servers' = '%s',
'properties.group.id' = '%s',
'format' = 'json'
)
Query:
query = """
with cte_flow_pdns as(
SELECT
stime as first_seen,
ltime as last_seen,
srcid,
cast(applabel['nanswers'] as int) as rec_count,
'struct' as type,
schema_json_array_generator() AS schema_sub_array,
uuid() as uuid
FROM %s
where applabel['appid'] = 'dns'
)
SELECT
ROW(type, schema_sub_array) schema,
ROW(first_seen,last_seen,srcid,rec_count,saddr,daddr,domain,response,rtype,rtype_str,uuid) pdns_record
FROM cte_flow_pdns
""" % (cfg['kafka_te_flows_source_table_name'] )Jagan Nalla
07/12/2023, 2:12 AM{
"schema": {
"type": "struct",
"fields": [
{
"type": "string",
"optional": true,
"field": "first_seen"
},
{
"type": "string",
"optional": true,
"field": "last_seen"
},
{
"type": "string",
"optional": true,
"field": "srcid"
},
{
"type": "int64",
"optional": true,
"field": "rec_count"
}
]
},
"pdns_record": {
"first_seen": "2023-07-12 02:03:01",
"last_seen": "2023-07-12 02:03:01",
"srcid": "mtcv",
"rec_count": 2
}
}Jagan Nalla
07/12/2023, 2:13 AMEugenio Gastelum
07/12/2023, 2:41 AM