Kafka Streams API GroupBy behaviour - json

I am new in kafka streams and I am trying to aggregate some streaming data into a KTable using groupBy function. The problem is the following:
The produced message is a json msg with the following format:
{ "current_ts": "2019-12-24 13:16:40.316952",
"primary_keys": ["ID"],
"before": null,
"tokens": {"txid":"3.17.2493",
"csn":"64913009"},
"op_type":"I",
"after": { "CODE":"AAAA41",
"STATUS":"COMPLETED",
"ID":24},
"op_ts":"2019-12-24 13:16:40.316941",
"table":"S_ORDER"}
I want to isolate the json field "after" and then create a KTable with "key" = "ID" and value the whole json "after".
Firstly, I created a KStream to isolate the "after" json, and it works fine.
KStream code block: (Don't pay attention to the if statement because "before" and "after" have the same format.)
KStream<String, String> s_order_list = s_order
.mapValues(value -> {
String time;
JSONObject json = new JSONObject(value);
if (json.getString("op_type").equals("I")) {
time = "after";
}else {
time = "before";
}
JSONObject json2 = new JSONObject(json.getJSONObject(time).toString());
return json2.toString();
});
The output, as expected, is the following:
...
null {"CODE":"AAAA48","STATUS":"SUBMITTED","ID":6}
null {"CODE":"AAAA16","STATUS":"COMPLETED","ID":1}
null {"CODE":"AAAA3","STATUS":"SUBMITTED","ID":25}
null {"CODE":"AAAA29","STATUS":"SUBMITTED","ID":23}
...
Afterwards, I implement a KTable to groupBy the "ID" of the json.
KTable code block:
KTable<String, String> s_table = s_order_list
.groupBy((key, value) -> {
JSONObject json = new JSONObject(value);
return json.getString("ID");
});
And there is an error that I want to create KTable<String, String> but I am creating GroupedStream<Object,String>.
Required type: KTable<String,String>
Provided:KGroupedStream<Object,String>
no instance(s) of type variable(s) KR exist so that KGroupedStream<KR, String> conforms to KTable<String, String>
In conclusion, the question is what exactly are KGroupedStreams and how to implement a KTable properly ?

After groupBy processor, you can use a stateful processor, like aggregate or reduce (that processors returns KTable). You can do something like this:
KGroupedStream<String, String> s_table = s_order_list
.groupBy((key, value) ->
new JSONObject(value).getString("ID"),
Grouped.with(
Serdes.String(),
Serdes.String())
);
KTable<String, StringAggregate> aggregateStrings = s_table.aggregate(
(StringAggregate::new),
(key, value, aggregate) -> aggregate.addElement(value));
StringAggregate looks like:
public class StringAggregate {
private List<String> elements = new ArrayList<>();
public StringAggregate addElement(String element){
elements.add(element);
return this;
}
//other methods
}

Related

Serializing scalar JSON in Flutter's Ferry Graphql for flexible Query

I have the following JSON scalar:
"""
The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSON
which I am trying to convert since my query is accepting input: JSON. When testing using graphql playground, query is JSON object thus the following works:
query {
carts(where: {
owner:{id: "xxx"}
store:{name: "yyy"}
}) {
id
}
}
# query is the starting from the where: {...}
# build.yaml
# build.yaml
gql_build|schema_builder: #same for gql_build|schema_builder + gql_build|var_builder + ferry_generator|req_builder:
options:
type_overrides:
DateTime:
name: DateTime
JSON:
name: BuiltMap<String, dynamic>
import: 'package:built_collection/built_collection.dart'
gql_build|serializer_builder:
enabled: true
options:
schema: myapp|lib/graphql/schema.graphql
custom_serializers:
- import: 'package:myapp/app/utils/builtmapjson_serializer.dart'
name: BuiltMapJsonSerializer
This is the custom serializer (builtmapjson_serializer.dart)
//// lib/app/utils/builtmapjson_serializer.dart
import 'package:built_collection/built_collection.dart';
import "package:gql_code_builder/src/serializers/json_serializer.dart";
class BuiltMapJsonSerializer extends JsonSerializer<BuiltMap<String, dynamic>> {
#override
BuiltMap<String, dynamic> fromJson(Map<String, dynamic> json) {
print('MyJsonSerializer fromJson: $json');
return BuiltMap.of(json);
}
#override
Map<String, dynamic> toJson(BuiltMap<String, dynamic> operation) {
print('MyJsonSerializer toJson: ${operation.toString()}');
return operation.asMap();
}
}
and the usage:
Future testQuery() async {
Map<String, dynamic> queryMap = {
"where": {
"owner": {
"id": "xxx",
"store": {"name": "yyy"}
}
}
};
final req = GFindCartsReq((b) {
return b..vars.query.addAll(queryMap);
});
var resStream = _graphQLService.client.request(req);
var res = await resStream.first;
print(
'linkExceptions: ${res.linkException}'); // Map: LinkException(Bad state: No serializer for '_InternalLinkedHashMap<String, Map<String, Object>>'.)
}
So whenever I try to query, it is throwing the linkException stated in the comment on the last line of usage. Any idea what should be the way of serializing it?
// Write query like this
query FindCarts($owner_id: String!, $store_name: String!) {
carts(where: {
owner:{id: $owner_id}
store:{name: $store_name}
}) {
id
}
}
// And make request like this:
final req = GFindCartsReq((b) => b..vars.store_name = 'XXX'..vars.owner_id = 'YYY');
I think you may be misunderstanding the use case. they are there to serialize and deserialize the response if you want to end up with a Dart object that's different from graphql representation. you may want to try rereading this section:
https://ferrygraphql.com/docs/custom-scalars/#create-a-custom-serializer
in the example in the docs, the graphql schema returns an int for the timestamp, but we want to actually use a Date object, so that's the purpose of the serializer. it tells ferry to deserialize the int in our responses to a Date so we can use a Date in our dart code. you could still use a json serializer (like in the examples you linked to) but it still would not be in the way you're trying to use it -- it would be if your schema returns a json string and you want to deserialize the json string. for example, in my case, my graphql schema actually does return a "jsonb" type on some objects. in order to handle that, i'm using built_value's default json_object like this:
(
...
type_overrides:
jsonb:
name: JsonObject
import: "package:built_value/json_object.dart"
custom_serializers:
- import: "package:built_value/src/json_object_serializer.dart"
name: JsonObjectSerializer

How do I loop over Json data?

This is my json structure.
[
[
{
"nos": 0,
"name": "A S MUSIC AND DANCE A CULTURAL ORGANIZATION",
"unique_id": "AN/2020/0259067",
"reg_details": [
{
"registered_with": "Registrar of Societies"
},
{
"type_of_ngo": "Registered Societies (Non-Government)"
This is working fine.
String jsonString = await _loadANgoAsset();
final jsonResponse = json.decode(jsonString);
String name = jsonResponse[0][0]['name'];
debugPrint("Name of NGO is $name");
But when I want to loop throug a key of various entities of data using this code:
List<dynamic> allNamesOfNGO = jsonResponse[0][0]['name'];
allNamesOfNGO.forEach((allNamesOfNGO) {
(allNamesOfNGO as Map<String, dynamic>).forEach((key, value) {
print(key);
(value as Map<String, dynamic>).forEach((key2, value2) {
print(key2);
print(value2);
});
});
});
The following error occurs:
E/flutter ( 4683): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: type 'String' is not a subtype of type 'List<dynamic>'
Help please!
List<dynamic> allNamesOfNGO = jsonResponse[0][0]['name'];
This line tries to assign name of first ngo(String) to allNamesOfNGO(List), which results in the above error.
To overcome this error, replace the above code with this:
List<dynamic> allNamesOfNGO = jsonResponse[0];
allNamesOfNGO.forEach((allNamesOfNGO) {
(allNamesOfNGO as Map<String, dynamic>).forEach((key, value) {
print(key);
print(value);
if(key == "reg_details") {
value.forEach(regDetail) {
print(regDetail);
});
}
});
});
You don't need innermost forEach loop as you have already got the key, value pair before that loop. In the innermost forEach loop, you are trying to loop through individual key like nos, name and unique_is(which is either a string or an int) which isn't possible.
Value of reg_details is a list. So you can loop through it, but for that you have to check if key == "reg_details".
because you declare it as String and then loop it as dynamic .try
List<string>
instead of
List<dynamic>.

How do you serialize/parse a nested JSON array in Flutter/Dart if it has no name/key

I'm working with a RESTful API that seems to be working and used in other applications that gives me something like this:
"notes": [
[
{
"automaticNote": false,
"contactId": 0,
"caseFileId": 0,
"dateCreated": "2019-05-02",
"deletedTime": "2019-05-02T19:31:54.588Z"
}
]
]
The double pair of square brackets means that one pair of the square brackets has no name/key associated with it. To make matters worse, notes is itself nested in some complex JSON.
I tried using JSON to Dart but it throws an error. So really my question is, how do I serialize a JSON array that has no key/name?
I'd normally do it like this:
class Note {
bool automaticNote;
int contactId;
int caseFileId;
String dateCreated;
String deletedTime;
Note(
{this.automaticNote,
this.contactId,
this.caseFileId,
this.dateCreated,
this.deletedTime});
Note.fromJson(Map<String, dynamic> json) {
automaticNote = json['automaticNote'];
contactId = json['contactId'];
caseFileId = json['caseFileId'];
dateCreated = json['dateCreated'];
deletedTime = json['deletedTime'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['automaticNote'] = this.automaticNote;
data['contactId'] = this.contactId;
data['caseFileId'] = this.caseFileId;
data['dateCreated'] = this.dateCreated;
data['deletedTime'] = this.deletedTime;
return data;
}
}
But the double JSON array is throwing me off (and again, notes itself is nested in a more complex JSON object but for the sake of simplicity I did not include the whole thing here).
Thanks!
After decoding, notes is a member of some Map<String, dynamic> as usual. Let's call that m.
So m['notes'] is a list, who's first member, m['notes'][0] is also a list. Its first member, m['notes'][0][0] is another Map<String, dynamic>, which is what you need for your constructor.
You should therefore be able to use:
Note n = Note.fromJson(m['notes'][0][0]);
Let's take the case of you saving automaticNote;
automaticNote = json['notes'][0][0]['automaticNote'];
This offers the key for notes which has an array where your data is at the first point or index 0 (of notes hence [0]) and within that array there is another array which has your data. Again it's the first array item so, [0] and then you're finally at the level with your data so you then use your key for automaticNote, ['automaticNote'].
Note.fromJson(Map<String, dynamic> json) {
automaticNote = json[0][0]['automaticNote'];
contactId = json[0][0]['contactId'];
caseFileId = json[0][0]['caseFileId'];
dateCreated = json[0][0]['dateCreated'];
deletedTime = json[0][0]['deletedTime'];
}

how to use a map in a JsonBuilder? i.e. how to create dynamic, not static Json in grails?

creating hard coded json is easy, e.g.
String createJson(Person person, list<Account> accounts) {
def builder = new JsonBuilder()
def json = builder {
person person
accounts accounts
}
return builder.toPrettyString()
}
The above works, and produces something like this:
{
"person":{
username": "user"
"firstName": "test"
}
"accounts":[
{
"balance": "200829.00",
"currency": "CRD",
"id": 1,
}
]
}
The problem is we have a REST api, which returns JSON. Curently, we have a lot of duplicate code, as we can't find a generic way to generate different parts of the JSON api response and combine them together and render the result, ether by merging json strings, or by dynamically buidling the json from a map, e.g the following doesnt work:
String createJson(Map map) {
def builder = new JsonBuilder()
def root = builder {
map.collect { key, value ->
"$key" value
}
}
return builder.toPrettyString()
}
then calling it like this:
Person person = someMethodToGetAPerson()
List<Account> accounts = someMethodToGetAccounts(person)
Map map = ["person", person, "accounts", accounts]
String json = createJson(map)
render(status: 200, contentType: 'application/json', text: json)
However, this fails, with a stack overflow in the bowels of grails.
In addition, we have defined several json marshallers which must be used, e.g.
JSON.registerObjectMarshaller(Account) {
return [balance: formatter.format(it.balance)....
}
Any ideas?
What I could understand is you want to convert a map into JSON string. For that you can use grails.converters.JSON class. For example
Person person = someMethodToGetAPerson()
List<Account> accounts = someMethodToGetAccounts(person)
Map map = [person: person, accounts: accounts]
String json = new JSON(map).toString()
The toString() method also takes an boolean value for preety printing. And it should honor your registered marshallers

Solr Json parsing on Client Side?

I am trying to retrieve date and corresponding count from a json below and it turns out that I just can't do it. After some struggle, I ended with the weird code below with nested linkedlists. How can I select solr_date and count as appearing at the very end : (I welcome any library that can do this)
{
"responseHeader":{
"status":0,
"QTime":2,
"params":{
"facet":"true",
"fl":" ",
"indent":"true",
"facet.query":" solr_date",
"q":"solr_body:party",
"facet.field":"solr_date",
"json.nl":"arrarr",
"wt":"json",
"fq":" "}},
"response":{"numFound":19,"start":0,"docs":[
{},
{},
{},
{},
{},
{},
{},
{},
{},
{}]
},
"facet_counts":{
"facet_queries":{
" solr_date":0},
"facet_fields":{
"solr_date":
[
["2013-06-19T13:48:02Z",10], *********************************
["2013-07-25T13:48:02Z",2],
["2013-07-27T13:48:02Z",2],
["2013-07-24T13:48:02Z",1], I need these numbers individually. Date and corresponding number.
["2013-07-26T13:48:02Z",1],
["2013-07-28T13:48:02Z",1],
["2013-07-29T13:48:02Z",1],
["2013-07-30T13:48:02Z",1]]}, ***************************
"facet_dates":{},
"facet_ranges":{}}}
Java code below :
ObjectMapper mapper = new ObjectMapper();
// JsonNode rootNode = m.readTree(new URL("http://173.255.245.138:8983/solr/collection1/select?q=*%3A*&wt=json&indent=true"));
Map<String, Object> mapObject = mapper.readValue(new URL("http://ipa.ddr.ess.000:8983/solr/collection1/select?q=solr_body%3Aparty&fq=+++&fl=+&wt=json&json.nl=arrarr&indent=true&facet=true&facet.query=+solr_date&facet.field=solr_date"),new TypeReference<Map<String, Object>>() {});
LinkedHashMap<String,LinkedHashMap<String,LinkedHashMap<String,ArrayList<String>>>> list = (LinkedHashMap<String, LinkedHashMap<String, LinkedHashMap<String, ArrayList<String>>>>) mapObject.get("facet_counts");
I would suggest using the SolrJ Client:
Solrj is a java client to access solr. It offers a java interface to add, update, and query the solr index.
If you're using Gson, and you're actually only interested in the part you highlighted, you could do a manual parsing. Something like this:
//Create parser and get the root object
JsonParser parser = new JsonParser();
JsonObject rootObj = parser.parse(json).getAsJsonObject();
//Get the solr_date array
JsonArray solrDateArray = rootObj
.getAsJsonObject("facet_counts")
.getAsJsonObject("facet_fields")
.getAsJsonArray("solr_date");
//Create arrays to store the data you want to retrieve
List<String> datesList = new ArrayList<>();
List<Integer> countsList = new ArrayList<>();
//Iterate the solr_date array
Iterator<JsonElement> it = solrDateArray.iterator();
while (it.hasNext()) {
//The solr_date array contains in turn arrays, so we parse each
JsonArray array = it.next().getAsJsonArray();
//and store in your Lists the values
datesList.add(array.get(0).getAsString());
countsList.add(array.get(1).getAsInt());
}
Now you'll have to List objects, one with all the dates and another with all the counts:
datesList: ["2013-06-19T13:48:02Z", "2013-07-25T13:48:02Z", 2013-07-27T13:48:02Z, ...]
countsList: [10, 2, 2, ...]
Note: instead of using 2 List objects, you could use a Map<Integer, String> for example...
i did it something like this.
HttpSolrServer server = new HttpSolrServer("http://localhost:8084/apache-solr-3.6.0/");
server.setParser(new XMLResponseParser());
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery("keyword");
solrQuery.setFilterQueries("keyword");
solrQuery.setHighlight(true);
solrQuery.setHighlightRequireFieldMatch(true);
solrQuery.addHighlightField("syndrome");
solrQuery.setStart(0);
solrQuery.setRows(10);
QueryResponse serverResponse = null;
try {
serverResponse = server.query(solrQuery);
} catch (SolrServerException e) {
e.printStackTrace();
}
Gson gson = new Gson();
List<SolrDocument> docs = new ArrayList<SolrDocument>();
for (SolrDocument doc:serverResponse.getResults()) {
docs.add(doc);
}
Map<String, String> pairs= new HashMap<String, String>();
Integer count = new Integer(0);
for (SolrDocument doc:docs){
pairs.put(("start_date" + count), doc.getFieldValue("start_date").toString());
pairs.put(("test_file_result_id" + count), doc.getFieldValue("test_file_result_id").toString());
pairs.put(("job_id" + count), doc.getFieldValue("job_id").toString());
pairs.put(("cluster" + count), doc.getFieldValue("cluster").toString());
pairs.put(("test_file_result_id" + count), doc.getFieldValue("test_file_result_id").toString());
count++;
}