Java elasticsearch spring-data set different index field name - java

I'm using elasticsearch java-api in combination with spring-data and have a problem with indexing a document.
I want a different name for an indexed field. That means not the same as in the java code:
Domainobject:
#Document(indexName = "testindex", type = "message")
public class MessageObject {
#Id
private String unid;
#FieldNameInElasticIndex(value = "javaMessage") // I want anything like that
private String message;
private String secondMessage;
private String thirdMessage;
...
getters & setters
...
}
Interface:
public interface MessageObjectRepository extends ElasticsearchRepository<MessageObject, Long> {
}
Service:
#Component
public class MessageService {
#Autowired
private MessageObjectRepository repository;
public void addRegistrationObject(MessageObject msg) {
repository.save(msg);
}
}
So....
is it possible to change the name so the index would look like:
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "testindex",
"_type": "message",
"_id": "00113B325ED357B7C1257E2D001D5B4B",
"_score": 1,
"_source": {
"unid": "00113B325ED357B7C1257E2D001D5B4B",
"javaMessage": "Hello", // <--- this is what I want (javaMessage)
"secondMessage": null,
"thirdMessage": "Third",
instead of
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "testindex",
"_type": "message",
"_id": "00113B325ED357B7C1257E2D001D5B4B",
"_score": 1,
"_source": {
"unid": "00113B325ED357B7C1257E2D001D5B4B",
"message": "Hello", // <--- this is NOT what I want (java name: message)
"secondMessage": null,
"thirdMessage": "Third",
?

The idea is simply to use the #JsonProperty annotation on the field and give the name you want to use during the JSON serialization:
#JsonProperty("javaMessage")
private String message;

Related

Spring Data Mongodb Aggregation - Group by nested objects and build DTO

I have the following Employee data in MongoDB
{
"_id": {
"$oid": "625f09bb1a96bf42ff4c4006"
},
"employeeId": 1234,
"email": "jason#acme.com",
"firstName": "Jason",
"lastName": "Stuart",
"currentCTC": 1201117.61,
"department": {
"$ref": "department",
"$id": {
"$oid": "625f09bb1a96bf42ff4c4005"
}
}
}
{
"_id": {
"$oid": "625f09bb1a96bf42ff4c4006"
},
"employeeId": 1235,
"email": "jasons#acme.com",
"firstName": "Jasons",
"lastName": "Stuarts",
"currentCTC": 1201117.61,
"department": {
"$ref": "department",
"$id": {
"$oid": "625f09bb1a96bf42ff4c4005"
}
}
}
My Spring #Document looks like this:
// Employee.java
#Data
#Document
public class Employee {
#Id
private String id;
private Long employeeId;
private String email;
private String firstName;
private String middleName;
private String lastName;
private Gender gender;
private double currentCTC;
#DBRef
private Department department;
}
// Department.java
#Document
#Data
public class Department {
#Id
private String id;
private String name;
}
Now, my requirement is to find the sum of salaries Department-wise.. I need the data to be in the following way:
[
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": "Engineering"
},
"cost": 31894773.01
},
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": "Marketing"
},
"cost": 4552325.25
}
]
I created an aggregate function like this in Spring Data:
public List<DepartmentCost> getDepartmentCosting() {
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$$ROOT").as("department");
Aggregation aggregation = Aggregation.newAggregation(groupByDepartment);
AggregationResults<DepartmentCost> results = mongoTemplate.aggregate(aggregation, "employee", DepartmentCost.class);
return results.getMappedResults();
}
And my expected DepartmentCost.java
#Data
#Document
public class DepartmentCost {
#DBRef
private Department department;
private double cost;
}
Now when I try this API out, I get the data correctly, but I do not get department name. It comes as null. I get a response like
[
{
"department": {
"id": "625f09bb1a96bf42ff4c4006",
"name": null,
},
"cost": 2241117.6100000003
},
{
"department": {
"id": "625f09bb1a96bf42ff4c400a",
"name": null,
},
"cost": 14774021.43
},
{
"department": {
"id": "625f09bc1a96bf42ff4c4013",
"name": null,
},
"cost": 14879633.97
}
]
How can I get the department details expanded in my model? Please help..
After a couple of attempts, I figured it out. All I had to do was this:
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$department").as("department");
as opposed to:
GroupOperation groupByDepartment = group("department").sum("currentCTC").as("cost").first("$$ROOT").as("department");

Is there any annotation for Spring data JPA to not return the entire entity in JSON? (Rest API (SQL))

Code: (It's simple)
#GetMapping("/{id}")
public Optional<Person> getOne(#PathVariable Long id){
return personRepository.findById(id);
}
Example:
{
"id": 1,
"name": "Will",
"character": {
"id": 1,
"name": "Batman",
"power": 100
}
}
Example of how I would like it:
(Only the entity id, without all fields.)
{
"id": 1,
"name": "Will",
"character": {
"id": 1
}
}
For that you can mark those fields as #Transient in entity. Those fields will neither be persisted nor be serialized, de-serialized..

Java Spring Deserializing Nested objects using RestTemplate

I am using Java Spring boot restTemplate and I am trying to deserialize the below JSON into their corresponding objects. However it is returning null.
Am I doing this the right way? Should I return a String response Entity and then convert?
{
"Events": [
{
"Id": 3584588,
"Url": "https://api.wildapricot.org/v2/accounts/257051/Events/3584588",
"EventType": "Regular",
"StartDate": "2019-10-07T07:00:00-05:00",
"EndDate": "2019-10-11T12:00:00-05:00",
"Location": "Renaissance Montgomery Hotel & Spa",
"RegistrationEnabled": false,
"RegistrationsLimit": null,
"PendingRegistrationsCount": 0,
"ConfirmedRegistrationsCount": 0,
"CheckedInAttendeesNumber": 0,
"InviteeStat": {
"NotResponded": 0,
"NotAttended": 0,
"Attended": 0,
"MaybeAttended": 0
},
"Tags": [
"event"
],
"AccessLevel": "AdminOnly",
"StartTimeSpecified": true,
"EndTimeSpecified": true,
"HasEnabledRegistrationTypes": false,
"Name": "2020 Montgomery IT Summit"
},
{
"Id": 3584591,
"Url": "https://api.wildapricot.org/v2/accounts/257051/Events/3584591",
"EventType": "Rsvp",
"StartDate": "2019-10-03T00:00:00-05:00",
"EndDate": "2019-10-31T00:00:00-05:00",
"Location": "Here",
"RegistrationEnabled": true,
"RegistrationsLimit": null,
"PendingRegistrationsCount": 0,
"ConfirmedRegistrationsCount": 0,
"CheckedInAttendeesNumber": 0,
"InviteeStat": {
"NotResponded": 0,
"NotAttended": 0,
"Attended": 0,
"MaybeAttended": 0
},
"Tags": [
"volunteer"
],
"AccessLevel": "Public",
"StartTimeSpecified": false,
"EndTimeSpecified": false,
"HasEnabledRegistrationTypes": true,
"Name": "Volunteer Event"
}
]
}
Here is my call:
ResponseEntity<WaEvents> response = restTemplate.exchange(uri,
HttpMethod.GET,
request,
WaEvents.class
);
return response.getBody().getEvents();
Here is my WaEvents Class:
#Data
public class WaEvents implements Serializable {
#JsonUnwrapped
#JsonProperty("Events")
private List<WaEvent> events;
}
Here is the WaEvent Class
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class WaEvent {
#JsonProperty("Id")
public Integer id;
#JsonProperty("Name")
public String name;
#JsonProperty("Location")
public String location;
#JsonProperty("StartDate")
public LocalDate startDate;
#JsonProperty("EndDate")
public LocalDate endDate;
#JsonProperty("IsEnabled")
public Boolean isEnabled;
#JsonProperty("Description")
public String description;
#JsonProperty("RegistrationLimit")
public Integer RegistrationLimit;
}
As explained here with an example :
public class Parent {
public int age;
public Name name;
}
public class Name {
public String first, last;
}
Without #JsonUnwrapped, the JSON is :
{
"age" : 18,
"name" : {
"first" : "Joey",
"last" : "Sixpack"
}
}
With #JsonUnwrapped, the JSON is :
{
"age" : 18,
"first" : "Joey",
"last" : "Sixpack"
}
So #JsonUnwrapped will flatten the properties and events won't exist anymore :
{
"Id": 3584588,
"Name": "2020 Montgomery IT Summit",
"Location": "Renaissance Montgomery Hotel & Spa",
"StartDate": "2019-10-07T07:00:00-05:00",
"EndDate": "2019-10-11T12:00:00-05:00",
...
}
Try to remove #JsonUnwrapped

Calling a Rest Service from another service for course registration system with spring boot

I was trying to make a REST call(Student, Course micro service) from my Registration micro service for inserting a new Registration but whenever i am calling those services Student micro service is showing the following error.
> org.springframework.web.client.RestClientException: Error while extracting response for type [class com.homeexam.registrationmanagementserver.model.Student] and content type [application/json;charset=UTF-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `com.homeexam.registrationmanagementserver.model.Student` out of START_ARRAY token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.homeexam.registrationmanagementserver.model.Student` out of START_ARRAY token
at [Source: (PushbackInputStream); line: 1, column: 1]
I tried my best to resolve the problem searching from google but i can't find any relevant solution regarding the problem. Help, me out!
This is My RegistrationService class where i am calling those micro services:
public Registration create(Registration registration) {
Student student = restTemplate.getForObject(studentsUrl, Student.class);
Section section = restTemplate.getForObject(sectionsUrl, Section.class);
Course course = restTemplate.getForObject(coursesUrl, Course.class);
/*registration = new Registration(registration.getId(), student.getId(),student.getProgram(),
section.getSectionNumber(),course.getCourseCode(),section.getFacultyInitial());*/
registration.setId(registration.getId());
registration.setStudentId(student.getId());
registration.setProgramName(student.getProgram());
registration.setCourseCode(course.getCourseCode());
registration.setFacultyInitial(section.getFacultyInitial());
registration.setSectionNumber(section.getSectionNumber());
Optional<Registration> optionalReg = registrationRepository.findById(registration.getId());
Optional<Registration> byCourseCode = registrationRepository.findById(Integer.valueOf(registration.getCourseCode()));
if (optionalReg.isPresent() ) {
throw new ResourceAlreadyExistsException();
} else if (byCourseCode.isPresent())
throw new AlreadyRegisteredException();
else {
return registrationRepository.save(registration);
}
}
But, code does work without calling the Micro service related info.
Registration JSON
{
"courseCode": "string",
"facultyInitial": "string",
"id": 0,
"programName": "string",
"sectionNumber": 0,
"studentId": "string"
}
Student JSON
{
"id": "201600000001",
"name": "UML Diagram",
"email": "uml#gmail.com",
"address": "Texas,USA",
"dob": "1998-09-11",
"mobileNumber": "01110000000",
"dateOfAdmission": "2018-09-10",
"program": "BSc. in EEE",
"role": "Student",
"password": "1555"
}
Course JSON
[
{
"courseCode": "CS193",
"courseTitle": "Android Application",
"courseCredit": 3,
"sectionList": [
{
"id": 1,
"sectionNumber": 1,
"seatLimit": 25,
"semesterNumber": 11,
"facultyInitial": "ATA",
"course": {
"courseCode": "CS193",
"courseTitle": "Android Application",
"courseCredit": 3,
"sectionList": null,
"gradeList": null
}
},
{
"id": 2,
"sectionNumber": 2,
"seatLimit": 30,
"semesterNumber": 11,
"facultyInitial": "ABA",
"course": {
"courseCode": "CS193",
"courseTitle": "Android Application",
"courseCredit": 3,
"sectionList": null,
"gradeList": null
}
}
],
"gradeList": null
}
]
Section JSON
[
{
"id": 1,
"sectionNumber": 1,
"seatLimit": 25,
"semesterNumber": 11,
"facultyInitial": "ATA",
"course": {
"courseCode": "CS193",
"courseTitle": "Android Application",
"courseCredit": 3,
"sectionList": null,
"gradeList": null
}
},
{
"id": 2,
"sectionNumber": 2,
"seatLimit": 30,
"semesterNumber": 11,
"facultyInitial": "ABA",
"course": {
"courseCode": "CS193",
"courseTitle": "Android Application",
"courseCredit": 3,
"sectionList": null,
"gradeList": null
}
}
]
Student.java
#Data
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode(of = {"id","name"})
#Document
public class Student {
#Id
private String id;
private String name;
private String email;
private String address;
private LocalDate dob;
private String mobileNumber;
private LocalDate dateOfAdmission;
private String program;
private Role role;
private String password;
}
Registration.java
public class Registration {
#Id
private int id;
private String studentId;
private String programName;
private int sectionNumber;
private String courseCode;
private String facultyInitial;
}
The error means that you send an array to the service that expects a single object.
Solution: Either send single JSON object per request or change the service so that it accepts array or list.
The problem is probably in the role property of Student. On your Student class, it expects to the receive a Role type, but in your JSON it's a String.
You have this:
{
"id": "201600000001",
"name": "UML Diagram",
"email": "uml#gmail.com",
"address": "Texas,USA",
"dob": "1998-09-11",
"mobileNumber": "01110000000",
"dateOfAdmission": "2018-09-10",
"program": "BSc. in EEE",
"role": "Student",
"password": "1555"
}
but should be something like this:
{
"id": "201600000001",
"name": "UML Diagram",
"email": "uml#gmail.com",
"address": "Texas,USA",
"dob": "1998-09-11",
"mobileNumber": "01110000000",
"dateOfAdmission": "2018-09-10",
"program": "BSc. in EEE",
"role": {
"type": "Student"
},
"password": "1555"
}
Since I don't know the Role class structure, I set the property "type" as an example.
Try this:
Student[] students = restTemplate.getForObject(studentsUrl, Student[].class);

Deserialize complex JSON to Java, classes nested multiple levels deep

I am trying to make the Json output from Cucumber into a single Java object. This contains objects nested four levels deep, and I am having trouble deserializing it. I am presently using Jackson, but open to suggestions.
Here is my Json code:
{
"line": 1,
"elements": [
{
"line": 3,
"name": "Converteren centimeters naar voeten/inches",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa;converteren-centimeters-naar-voeten/inches",
"type": "scenario",
"keyword": "Scenario",
"steps": [
{
"result": {
"duration": 476796588,
"status": "passed"
},
"line": 4,
"name": "maak Maten-object aan met invoer in \"centimeters\"",
"match": {
"arguments": [
{
"val": "centimeters",
"offset": 37
}
],
"location": "StepDefinition.maakMatenObjectAanMetInvoerIn(String)"
},
"keyword": "Given "
},
{
"result": {
"duration": 36319,
"status": "passed"
},
"line": 5,
"name": "ik converteer",
"match": {
"location": "StepDefinition.converteerMaten()"
},
"keyword": "When "
},
{
"result": {
"duration": 49138,
"status": "passed"
},
"line": 6,
"name": "uitvoer bevat maat in \"voeten/inches\"",
"match": {
"arguments": [
{
"val": "voeten/inches",
"offset": 23
}
],
"location": "StepDefinition.uitvoerBevatMaatIn(String)"
},
"keyword": "Then "
}
]
},
{
"line": 8,
"name": "Converteren voeten/inches naar centimeters",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa;converteren-voeten/inches-naar-centimeters",
"type": "scenario",
"keyword": "Scenario",
"steps": [
{
"result": {
"duration": 84175,
"status": "passed"
},
"line": 9,
"name": "maak Maten-object aan met invoer in \"voeten/inches\"",
"match": {
"arguments": [
{
"val": "voeten/inches",
"offset": 37
}
],
"location": "StepDefinition.maakMatenObjectAanMetInvoerIn(String)"
},
"keyword": "Given "
},
{
"result": {
"duration": 23928,
"status": "passed"
},
"line": 10,
"name": "ik converteer",
"match": {
"location": "StepDefinition.converteerMaten()"
},
"keyword": "When "
},
{
"result": {
"duration": 55547,
"status": "passed"
},
"line": 11,
"name": "uitvoer bevat maat in \"centimeters\"",
"match": {
"arguments": [
{
"val": "centimeters",
"offset": 23
}
],
"location": "StepDefinition.uitvoerBevatMaatIn(String)"
},
"keyword": "Then "
}
]
}
],
"name": "Applicatie neemt maten in cm en converteert ze naar voet/inch, en vice versa",
"description": "",
"id": "applicatie-neemt-maten-in-cm-en-converteert-ze-naar-voet/inch,-en-vice-versa",
"keyword": "Feature",
"uri": "sample.feature"
}
I have tried a number of different approaches. First I used nested inner classes, but it appeared you had to make them static, which I feared would not work since I have multiple instances of the same object within one (multiple "element"-objects in the root, for example). Then I tried putting them in separate classes, with Json annotations. Here's where that got me (omitting setters):
public class CucumberUitvoer {
private String name;
private String description;
private String id;
private String keyword;
private String uri;
private int line;
#JsonProperty("elements")
private List<FeatureObject> elements;
public CucumberUitvoer(){}
}
public class FeatureObject {
private String name;
private String description;
private String id;
private String type;
private String keyword;
private int line;
#JsonProperty("steps")
private List<StepObject> steps;
public FeatureObject() {
}
}
public class StepObject {
#JsonProperty("result")
private ResultObject result;
private String name;
private String given;
private String location;
private String keyword;
private int line;
#JsonProperty("match")
private MatchObject match;
public StepObject(){}
}
public class ResultObject {
private int duration;
private String status;
public ResultObject(){}
}
public class MatchObject {
#JsonProperty("arguments")
private List<ArgumentObject> arguments;
private String location;
public MatchObject(){}
}
public class ArgumentObject {
private String val;
private String offset;
public ArgumentObject(){}
}
For clarification, here's a class diagram of how the nesting works.
This solution gives me the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of nl.icaprojecten.TestIntegratieQuintor.JSONInterpreter.CucumberUitvoer out of START_ARRAY token
Here is the code doing the actual mapping:
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
CucumberUitvoer obj1 = null;
try {
obj1 = mapper.readValue(json, CucumberUitvoer.class);
} catch (IOException e) {
e.printStackTrace();
}
Is there a quick fix to this approach to make it work, or should I try something entirely different?
Ok I spent some time debugging and trying to figure out what was the problem, and finally was something pretty obvious.
implements Serializable
Thats the line I added to MatchObject and worked.
When we try to deserialize some object first we have to make those classes implements the interface Serializable
I just tried your sample code and oddly, it works.
Can you please double check your imports, if the JSON is coming in as provided and the getters, setters, constructors are actually there?
You can get the idea from this code to deserialize,
public class testCustomDeSerializer extends JsonDeserializer<test> {
public testCustomDeSerializer() {
this(null);
}
public TestCustomDeSerializer(Class t) {
// super(t);
}
#Override
public Test deserialize(JsonParser p, DeserializationContext ctx) throws IOException, JsonProcessingException {
ObjectCodec objectCodec = p.getCodec();
JsonNode node = objectCodec.readTree(p);
ObjectMapper objectMapper = new ObjectMapper();
Test test= new Test();
test.setId(node.get("line").asText());
List<elements> elementList = new ArrayList<>();
JsonNode elementsNode = node.get("elements");
Iterator<JsonNode> slaidsIterator = elementsNode.elements();
while (slaidsIterator.hasNext()) {
Steps steps= new Steps();
JsonNode slaidNode = slaidsIterator.next();
JsonNode stepNode= (JsonNode) slaidNode.get("Steps");
BoundingPoly in = objectMapper.readValue(stepNode.toString(), Steps.class);
elementsNode.setSteps(in);
/// continue
return
}
Hope it helps

Categories