Mongo Spark Java Connector Group by - spring-boot

i am storing events on my server from client mobile application, event store is mongodb.
I have mongo-spark connector which fetches list of these events and should display them using rest api. It should be streaming for later, but for now i am trying to display it as a single call.
So far i have written my controller as given below:
#RestController
#RequestMapping("/analytics")
class EventController #Autowired constructor(val eventMongoServiceImpl: EventMongoServiceImpl,
val javaSparkContext: JavaSparkContext) {
#GetMapping("/event")
fun getEvent(): ResponseEntity<EventResponse> {
val customRdd: JavaMongoRDD<Document> = MongoSpark.load(javaSparkContext)
val toDF = customRdd.toDF()
}
}
Please help me filter these result given below for rest api:
[
{
"key": "Event A",
"description": "Event A Description",
"count": 3
},
{
"key": "Event B",
"description": "Event B Description",
"count": 0
}
]
I have a data set as given below:
/* 1 */
{
"_id" : ObjectId("5e61e38eb8425d3b1c7679ea"),
"name" : "Event A",
"description" : "Event A Description",
"date" : ISODate("2020-03-05T18:30:00.000Z"),
"_class" : "x"
}
/* 2 */
{
"_id" : ObjectId("5e61e416b8425d3b1c7679ec"),
"name" : "Event A",
"description" : "Event A Description",
"date" : ISODate("2020-03-05T18:30:00.000Z"),
"_class" : "x"
}
/* 3 */
{
"_id" : ObjectId("5e61e47fb8425d3b1c7679ee"),
"name" : "Event A",
"description" : "Event A Description",
"date" : ISODate("2020-03-05T18:30:00.000Z"),
"_class" : "x"
}

You should be able on the dataframe to make something like this
val aggDf = toDf
.groupBy("name")
.agg(count("name"), max("description"))
Now on the new dataframe aggDf you can do aggDf.toJson and have the results. If columns does not match the output you can tweak them with withColumnRenamed

Related

Add a json attribute in a flow content with Jolt Transformation or alternative way in NiFi

I need to add this attribute named 'metadata' to json flow content.
The attribute 'metadata' is like:
{"startTime":1451952013663, "endTime":1453680013663, "name":"Npos19", "deleted":false}
The input is like this:
{
"id": 154299613718447,
"values": [
{
"timestamp": 1451977869683,
"value": 13.1
},
{
"timestamp": 1453949805784,
"value": 7.54
}
]
}
My goal is like:
{
"id": 154299613718447,
"values": [ {
"startTime":1451952013663,
"endTime":1453680013663,
"name":"Npos19",
"deleted":false,
"timestamp": 1451977869683,
"value": 13.1
},
{
"startTime":1451952013663,
"endTime":1453680013663,
"name":"Npos19",
"deleted":false,
"timestamp": 1453949805784,
"value": 7.54
}
]
}
I tried to use the Jolt Transformation:
{
"operation": "default",
"spec": {
// extract metadata array from json attribute and put it in a temporary array
"tempArray": "${metadata:jsonPath('$.*')}"
}
}
but it does not work. I need to extract metadata array with $.* because I do not know what keys will be present.
Is there an alternative fast way with other nifi processors to merge the attribute with flow content?
thanks in advance
It's possible with combination of two processors: EvaluateJsonPath ->ScriptedTransformRecord.
EvaluateJsonPath
Destination: flowfile-attribute
Return Type: json
values (dynamic property): $.values
ScriptedTransformRecord
Record Reader: JsonTreeReader
Record Writer: JsonRecordSetWriter
Script Language: Groovy
Script Body:
def mapMetadata = new groovy.json.JsonSlurper().parseText(attributes['metadata'])
def mapValue = new groovy.json.JsonSlurper().parseText(attributes['values'])
def values = mapValue.each { value ->
mapMetadata.each { k, v ->
value."${k}" = v
}
}
record.setValue('values', null)
record.setValue('updateValues', values)
record
Output json
[ {
"id" : 154299613718447,
"values" : null,
"updateValues" : [ {
"timestamp" : 1451977869683,
"value" : 13.1,
"startTime" : 1451952013663,
"endTime" : 1453680013663,
"name" : "Npos19",
"deleted" : false
}, {
"timestamp" : 1453949805784,
"value" : 7.54,
"startTime" : 1451952013663,
"endTime" : 1453680013663,
"name" : "Npos19",
"deleted" : false
} ]
} ]

Spring Data MongoDB - Aggregation with other collection as well

I'm working on Spring Boot v.2.1.3.RELEASE and Spring Data MongoDB. Due to critical requirement, I had to model like below assuming Employee knows multiple technologies, but primary language would be anyone.
So, I decided to keep technology collection separate and somehow related Employee and technology in the Employee Collection.
{
"_id" : ObjectId("5ec65750fdcd4e960f4b2f24"),
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"status" : "A"
}
So, I've done association like below -
Note: Multiple Technologies can be associated with one Employee
One Employee can be associated with multiple technologies
Employee can have one and only one Primary Technology
{
"_id" : ObjectId("5ec507c72d8c2136245d35ce"),
"firstName" : "John",
"lastName" : "Doe",
"email" : "j.d#gmail.com",
.......
.......
.......
"employeeTechnologyRefs" : [
{
"technologyCd" : "AB",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "AB",
"primaryTechnologySw" : "N",
"Active" : "A"
},
{
"technologyCd" : "PR",
"primaryTechnologySw" : "N",
"Active" : "A"
},
{
"technologyCd" : "PR",
"primaryTechnologySw" : "N",
"Active" : "A"
}
],
"countryPhoneCodes" : [
"+352"
],
....
...
}
I used below query, how to query over the Technology documents to get the result and map it and create final object?
Criteria criteria = new Criteria();
criteria.andOperator(
StringUtils.isNotBlank(firstName) ? Criteria.where("firstName").is(firstName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(lastName) ? Criteria.where("lastName").is(lastName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(email) ? Criteria.where("email").is(email.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(technologyCd) ? Criteria.where("employeeTechnologyRefs.technologyCd").is(technologyCd.toUpperCase())
: Criteria.where(""));
MatchOperation matchStage = Aggregation.match(criteria);
GroupOperation groupOp = Aggregation
.group("firstName", "lastName", "email","_id")
.addToSet("employeeTechnologyRefs").as("employeeTechnologyRefs");
ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs");
Aggregation aggregation = Aggregation.newAggregation(matchStage, groupOp, projectStage);
AggregationResults<CustomObject> results = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Employee.class), CustomObject.class);
System.out.println(results);
Result should look like below
{
"_id" : ObjectId("5ec507c72d8c2136245d35ce"),
"firstName" : 442,
"lastName" : "LU",
"email" : "LUX",
.......
.......
.......
"employeeTechnologyRefs" : [
{
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "AB",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "PR",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
},
{
"technologyCd" : "PR",
"technologyCd" : "AB",
"technologyName" : "My ABC",
"ltechnologyNativeName" : "XY",
"primaryTechnologySw" : "Y",
"Active" : "A"
}
],
....
}
If you use below lookup operation in your code, you should able to get the answer as expected, and you don't need group operation in your code.
Edited Answer: This is how whole code should look like. One more thing, you don't need projection and if you need try projecting specific field only, and as part of the lookup operation do not use the same field as it is in your collection, otherwise it will override existing data from employee collection.
Criteria criteria = new Criteria();
criteria.andOperator(
StringUtils.isNotBlank(firstName) ? Criteria.where("firstName").is(firstName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(lastName) ? Criteria.where("lastName").is(lastName.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(email) ? Criteria.where("email").is(email.toUpperCase())
: Criteria.where(""),
StringUtils.isNotBlank(technologyCd) ? Criteria.where("employeeTechnologyRefs.technologyCd").is(technologyCd.toUpperCase())
: Criteria.where(""));
MatchOperation matchStage = Aggregation.match(criteria);
/*GroupOperation groupOp = Aggregation
.group("firstName", "lastName", "email","_id")
.addToSet("employeeTechnologyRefs").as("employeeTechnologyRefs");
*/
LookupOperation lookupOperation = LookupOperation.newLookup().
from("technology_collection_name").
localField("employeeTechnologyRefs.technologyCd").
foreignField("technologyCd").
as("employeeTechnologyRefsOtherName");
/* ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs");
*/
// And if you want to project specific field from employee array you can use something like.
ProjectionOperation projectStage = Aggregation.project("employeeTechnologyRefs.fieldName")
Aggregation aggregation = Aggregation.newAggregation(matchStage, lookupOperation, projectStage);
AggregationResults<CustomObject> results = mongoTemplate.aggregate(aggregation, mongoTemplate.getCollectionName(Employee.class), CustomObject.class);
System.out.println(results);

Transform string to json in Laravel api

Two month ago, i created an api in laravel and tested it with postman. Everything worked fine.
Now i would continue to develop, but i can't access the elements like before.
Postman:
Body:
{
"RFQ" : "123",
"client_id": "2",
"ITEMS": [
{
"material" : "1.234.565",
"description" : "Test material 1",
"quantity" : "2.123",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "2018-01-12",
},
{
"material" : "9.87564.2",
"description" : "Test material 2",
"quantity" : "4",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "15.01.2018"
}
]
}
Controller:
public function import(ImportRequestForQuoteRequest $request, $id)
{
return $request->getContent();
}
Before, i was able to get as example the client_id like $request->client_idbut now it returns nothing.
If i return $request->getContent()i get a string like the body.
What i have to do to access the values?
Try to return it like this
public function import(ImportRequestForQuoteRequest $request, $id)
{
return response()->json($request->getContent());
}
Source docs: https://laravel.com/docs/5.6/responses#json-responses
You can try this in you controller...
use Illuminate\Http\Request;
public function myFunction(Request $request, $id)
{
$post_param = $request->post_param;
$default = '';
$post_param = $request->input('client_id', $default);
$route_param = $id;
return response()->json(['params' => $request->all()]);
}
you need to decode json to php array and then you able to access like normal array in php
$item = json_decode($request->ITEMS);
Your Body:
{
"RFQ" : "123",
"client_id": "2",
"ITEMS": [
{
"material" : "1.234.565",
"description" : "Test material 1",
"quantity" : "2.123",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "2018-01-12",
},
{
"material" : "9.87564.2",
"description" : "Test material 2",
"quantity" : "4",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "15.01.2018"
}
]
}
Change with :
{
"RFQ" : "123",
"client_id": "2",
"ITEMS": [
{
"material" : "1.234.565",
"description" : "Test material 1",
"quantity" : "2.123",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "2018-01-12"
},
{
"material" : "9.87564.2",
"description" : "Test material 2",
"quantity" : "4",
"Quot. Deadline" : "2018-01-12",
"delivery_date" : "15.01.2018"
}
]
}
json array should be well-formed. So try to remove comma from array at "delivery_date" of first item.
and you will get results using $request->all().
Hope this solves.
I did the same request with the php storm REST Client. With the same body and headers, everything works fine.
Postman adds before and after the content """ I don't know why.
So the error is anywhere in Postman

FHIR complex extension

How can I add an extension that has multiple fields ?
For example if I have a extension related to visit motives that is structured like this in my application :
"visit_motive":
{
"id": "1",
"label": "Visit motive name",
"color": "#000000",
"duration": 5
}
I have something like this for the moment :
"extension": [
{
"url": "https://api.test.com/fhir/StructureDefinition/schedule-visit_motives",
"valueIdentifier": "visit_motive1_id",
"valueString" : "visit motive name",
"valueString" : "#000",
"valueInteger" : 5,
},
{
"url": "https://api.test.com/fhir/StructureDefinition/schedule-visit_motives",
"valueIdentifier": "visit_motive2_id",
"valueString" : "visit motive name 2",
"valueString" : "#111",
"valueInteger" : 10,
}
]
But I'm pretty sure it is not correct since I can't name the fields since I have to precise value[x] each time.
How can I do it ?
You would do that with a complex extension, which is basically an extension containing extensions. You can see more information about extensions in the spec. Scroll down a bit for the complex one. Or view an example of the structure of a complex extension here.
The use of your extension would look something like this:
"extension" : [{
"url" : "https://api.test.com/fhir/StructureDefinition/schedule-visit_motives",
"extension" : [{
"url" : "id",
"valueIdentifier": "visit_motive1_id"
}, {
"url" : "label",
"valueString" : "visit motive name"
}, {
"url" : "color",
"valueString" : "#000"
}, {
"url" : "duration",
"valueInteger" : 5,
}]
}]

Mongodb java - how to delete particular document

{
CONTENT1:{
YDXM:[{
"name":"1",
"MBNH":"1"}
{"name":"2",
"MBNH":"2"}]
}
I want to delete the {"name":"1","MBNH":"1"}. How can I achieve this?
Assuming that the following is your document and you want to delete the ENTIRE document:
{
"CONTENT1": {
"YDXM": [
{
"name": "1",
"MBNH": "1"
},
{
"name": "2",
"MBNH": "2"
}
]
}
}
You could use this:
db.test.remove({"CONTENT1.YDXM.name" : "1", "CONTENT1.YDXM.MBNH" : "1"})
Now, if you want to extract the document {"name" : "1", "MBNH" : "1"} from the CONTENT1.YDXM array, you should use the $pull operator:
db.test.update({"CONTENT1.YDXM.name" : "1", "CONTENT1.YDXM.MBNH" : "1"}, { $pull : { "CONTENT1.YDXM" : {"name" : "1", "MBNH" : "1"} } }, false, true)
This will perform an update in all documents that matches with the first argument. The second argument, with the $pull operator, means that the mongodb will remove the value {"name" : "1", "MBNH" : "1"} from CONTENT1.YDXM array.
You could read more about $pull operator and the update command in this links:
http://docs.mongodb.org/manual/reference/operator/pull/
http://docs.mongodb.org/manual/applications/update/

Resources