Spring Data ES query by nested object date field - spring

Using Spring Data ES.
I have an index named ship with following definition:
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
#Document(indexName = "ship", type = "journey")
public class ShipJourney {
#Id
private String shipId;
#Field(type = FieldType.Nested)
private List<ShipLocation> shipLocation;
//getters and setters
}
ShipLocation is a nested object defined as:
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.time.LocalDateTime;
public class ShipLocation {
private double latitude;
private double longitude;
#Temporal(TemporalType.TIMESTAMP)
private LocalDateTime recordedOnTime;
//setters and getters
}
I want to find a ship location just before or equal to LocalDateTime parameter.
I tried this:
ShipJourney findTopByShipIdAndShipLocationRecordedOnTimeLessThanOrderByShipLocationRecordedOnTimeDesc(
String shipId, LocalDateTime recordedOnTime);
only to realise later that I was using Top on the ShipJourney itself which will be eventually one record only due to the shipId's uniqueness.
How can I limit the data I get for the nested element based on one of its attribute?

I'm not sure if it's possible with the Spring Data ElasticSearch repositories. If you can autowire ElasticSearchTemplate, your query could look something like this (untested):
String shipId = "A";
LocalDateTime recordedOnTime = LocalDateTime.now();
int maxShipLocations = 3; // <-- TOP
NestedQueryBuilder findByLocation = nestedQuery("shipLocation", rangeQuery("shipLocation.recordedOnTime").lt(recordedOnTime));
TermQueryBuilder findById = termQuery("id", shipId);
QueryBuilder findByIdAndLocation = QueryBuilders.boolQuery()
.filter(findById)
.filter(findByLocation.innerHit(new QueryInnerHitBuilder().setSize(maxShipLocations)));
SearchQuery query = new NativeSearchQueryBuilder()
.withIndices("ship")
.withTypes("journey")
.withQuery(findByIdAndLocation)
.withSort(SortBuilders.fieldSort("shipLocation.recordedOnTime").order(SortOrder.DESC))
.build();
List<ShipJourney> shipJourney = elasticSearchTemplate.queryForList(query, ShipJourney.class);

Related

No property .. found for type .. in spring boot

I'm a beginner with spring and I have this little issue. "No property questionId found for type CourseTestCompleteField!" I have 2 model classes that are connected via a one to one join.
That 2 model class are:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "questions")
public class CourseTestQuestion {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="question_id")
private Long id;
#NotBlank
#Column(name = "question_course")
private String questionCourse;
#NotBlank
#Column(name = "question_type")
private String questionType;
public CourseTestQuestion(){
}
public CourseTestQuestion(String questionCourse, String questionType) {
this.questionCourse = questionCourse;
this.questionType = questionType;
}
// public getters and setters for all fields here
}
And:
package com.example.springboot.models;
import javax.persistence.*;
import javax.validation.constraints.NotBlank;
#Entity
#Table(name = "quiz_complete_field_questions",
uniqueConstraints = {
#UniqueConstraint(columnNames = "question_id")
}
)
public class CourseTestCompleteField {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Column(name = "question_content")
private String questionContent;
#NotBlank
#Column(name = "answer")
private String answer;
#NotBlank
#Column(name = "points")
private String points;
#NotBlank
#Column(name = "course")
private String course;
#NotBlank
#Column(name = "teacher_username")
private String teacher;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "question_id", referencedColumnName = "question_id")
private CourseTestQuestion courseTestQuestion;
public CourseTestCompleteField(){
}
public CourseTestCompleteField(CourseTestQuestion courseTestQuestion, String question, String answer, String points, String course, String teacher) {
this.courseTestQuestion = courseTestQuestion;
this.questionContent = question;
this.answer = answer;
this.points = points;
this.course = course;
this.teacher = teacher;
}
// public getters and setters for all fields here
}
My repo for both:
package com.example.springboot.repository;
import com.example.springboot.models.Course;
import com.example.springboot.models.CourseTestQuestion;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
#Repository
public interface CourseTestQuestionRepository extends JpaRepository<CourseTestQuestion, Long> {
Optional<CourseTestQuestion> findById(Long id);
Optional<CourseTestQuestion> findByQuestionCourse(String questionCourse);
}
And:
package com.example.springboot.repository;
import com.example.springboot.models.CourseTestCompleteField;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
#Repository
public interface CourseTestCompleteFieldRepository extends JpaRepository<CourseTestCompleteField, Long> {
Optional<CourseTestCompleteField> findById(Long id);
Optional<CourseTestCompleteField> findByQuestionId(Long questionId);
Optional<CourseTestCompleteField> findByCourse(String course);
List<CourseTestCompleteField> findByQuestionContent(String questionContent);
List<CourseTestCompleteField> findByTeacher(String teacher);
Boolean existsByQuestionContent(String questionContent);
}
The problem is with Optional<CourseTestCompleteField> findByQuestionId(Long questionId);but I don't get it why, because in database I have the table for CourseTestCompleteFieldModel with question_id column, and in CourseTestCompleteField I have CourseTestQuestion object. Tho, the table for CourseTestCompleteField has a different name, could be this a problem? I should rename the table to course_test_complete_field?
Can someone help me please? Thank you
Since,This is a query on nested Object. You need to update your query as this.
Optional<CourseTestCompleteField> findByCourseTestQuestion_Id(Long questionId);
This works even without "_"
Optional<CourseTestCompleteField> findByCourseTestQuestionId(Long questionId);
But better to put "_" while accessing nested fields for better readability.
There is no field call questionId in you entity and you have id only.
That's you got error. You can use that findyById(). That's only enough.
If you would like write JPA repository method like findBy..., getBy..., deleteBy...., countBy..., After this you need append exact field name from entity.
For example if you entity have name then can write below methods. findByName(); deleteByName(); countByName();
So try as below.
findBycourseTestQuestion(Object o);
Pass questions object.

How to fetch data from multiple tables and put into an custom object? spring boot?

JpaRepository
public interface SessionsStoreRepository extends JpaRepository<SessionsStore, Integer> {
SessionsStore findBySessionId(String sessionId);
//JPQL
#Query(value = "SELECT s.username,s.loginTime,s.logoutTime, e.firstName, e.lastName from SessionsStore s, Employee e "
+ " where e.username = s.username and s.isActive= 1")
List<ActiveSession> findUsers();
}
ActiveSession
import java.time.LocalDateTime;
import lombok.Data;
#Data
public class ActiveSession {
private String username;
private String firstName;
private String lastName;
private LocalDateTime loginTime;
private LocalDateTime logoutTime;
}
How can I store the data fetch by #Query(...) in ActiveSession Object Or is there any other way? I tried storing data into object (i.e List<Object>) but I'm using ftl for view and it does not allow you fetch the data unless the object have getters.
you can use a class-based projection to achive this.
More information here, or there is a good example of how to use it here

Problem Parsing request body of type json, containing a list of string to Flux of string in Spring reactive

I have a DTO as below:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import reactor.core.publisher.Flux;
#Data
#NoArgsConstructor
#AllArgsConstructor
public class InternetPackageDto {
private String id;
private String name;
private String termsAndConditions;
private String price;
private Flux<String> packageAttributes;
private Flux<String> extras;
}
And a Database Object as below:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import reactor.core.publisher.Flux;
#Document("internet_packages")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class InternetPackage {
#Id
private String id;
private String name;
private String termsAndConditions;
private String price;
private Flux<StoreableAttribute> attributes;
private Flux<StoreableAttribute> extras;
}
The StorableAttribute Database Model like so:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
#Document("package_attributes")
#Data
#NoArgsConstructor
#AllArgsConstructor
public class StoreableAttribute {
#Id
private String id;
private String name;
private String description;
}
On the Data Object the fields: Flux<StoreableAttribute> attributes and Flux<StoreableAttribute> extras are stored in a separate collection alongside the Package Object. And is handled by the mapper as below:
public InternetPackage fromDto(InternetPackageDto dto) {
var internetPackage = new InternetPackage();
internetPackage.setName(dto.getName());
internetPackage.setPrice(dto.getPrice());
internetPackage.setId(dto.getId());
internetPackage.setExtras(this.resolePackageExtras(dto));
internetPackage.setAttributes(this.resolePackageAttributes(dto));
return internetPackage;
}
private Flux<StoreableAttribute> resolePackageExtras(InternetPackageDto dto) {
return this.storeableAttributeService.resolveAttributes(dto.getExtras());
}
for the extra and similarly for the attributes also.
And a simple controller method as below:
#PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public Mono<InternetPackageDto> update(#RequestBody InternetPackageDto incomingPackageDto) {
return this.packageService
.updatePackage(this.dtoMapper.fromDto(incomingPackageDto))
.map(this.dtoMapper::toDto);
}
And when I make a post request I get an error stating
org.springframework.core.codec.CodecException: Type definition error: [simple type, class reactor.core.publisher.Flux]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `reactor.core.publisher.Flux` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
at [Source: (io.netty.buffer.ByteBufInputStream); line: 2, column: 13] (through reference chain: com.example.api.dto.InternetPackageDto["extras"])
Some more information:
I am using the class InternetPackageDto as a request object as well as a response object.
I am using Flux<String> and not List<String> since I wasn't sure if doing blocking resolution to list was a good idea.
The attributes are stored and managed separately.
And during the time of updating or inserting the package those; if a new extra or attribute is included the attributes collection in db will be updated with the insertion of new incoming extras and attributes.
It seems like I might have made a stupid mistake because I cannot find much information about this problem, or I am doing it completely wrong.
Any help would be greatly appreciated.
I think you should do smth like this
public Mono<InternetPackageDto> toDto(InternetPackage entity) {
var internetPackage = new InternetPackageDto();
internetPackage.setName(entity.getName());
internetPackage.setPrice(entity.getPrice());
internetPackage.setId(entity.getId());
return Mono.zip(Mono.just(internetPackage), entity.getExtras().collectList(), entity.getAttributes().collectList())
.flatMap(tu->{
var dto = tu.getT1();
dto.setExtras(tu.getT2()); //To make it work in my local i made entity.getAttributes() as Flux<String> so here you will probably need to use .stream().map(dbItem->dbItem.getPropertyName())
dto.setPackageAttributes(tu.getT2());
return Mono.just(dto);
});
}

Spring Data Elasticsearch Query Merge

I have a application for get a location from given points' 10 km radius and I have two filters for query; first is query by customer no and the second one is query by filtered items location according to. I want to combine elastic custom query and spring data method name based query. How can I merge them?
My document:
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
#Getter
#Setter
#Document(indexName = "customer", replicas = 0, refreshInterval = "-1")
public class Customer {
#GeneratedValue(strategy= GenerationType.AUTO)
#Id
private String id;
private Integer cifNo;
private String userId;
private String name;
private Integer locationCount;
private Date lastSeenDate;
#GeoPointField
private GeoPoint geoPoint;
}
Repository;
#Repository
public interface CustomerLocationRepository extends ElasticsearchRepository<CustomerLocation,Long> {
List<CustomerLocation> findByCifNoAndUserIdIsNullOrderByLastSeenDateDesc(Integer cifNo);
List<CustomerLocation> findByCifNoAndUserIdOrderByLastSeenDateDesc(Integer clientNo, String userId);
}
Service:
public List<CustomerLocation> getCustomersPlacesIndexWithinLocation(GenericRequest genericRequest) {
GeoDistanceQueryBuilder geoDistanceQueryBuilder = QueryBuilders
.geoDistanceQuery("geoPoint")
.point(genericRequest.getLatitude(),genericRequest.getLongitude())
.distance(10, DistanceUnit.KILOMETERS);
List<CustomerLocation> customerLocationList;
if(genericRequest.getUserId()!=null) {
customerLocationList = customerLocationRepository.findByCifNoAndUserIdOrderByLastSeenDateDesc(Integer.valueOf(genericRequest.getClientNo()),genericRequest.getUserId());
} else {
customerLocationList = customerLocationRepository.findByCifNoAndUserIdIsNullOrderByLastSeenDateDesc(Integer.valueOf(genericRequest.getClientNo()));
}
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withFilter(geoDistanceQueryBuilder)
.build();
return elasticsearchTemplate.queryForList(searchQuery,CustomerLocation.class);
}
How can I combine spring data elasticsearch query result with custom location search query?
Currently this is not possible with Spring Data Elasticsearch. I added a Jira issue for this to be implemented as a new feature.

Nested property not found in a Spring Data query method declaration

I'm trying to use findBy... in my repository to get a Savingaccount object passing a nested attribute(name) as a parameter. Currently I'm using:
Mono<SavingAccount> findByOwnerName(String name);
but I'm getting this error: No property name found for type Owner! Traversed path: SavingAccount.owner.
My repository:
package com...SavingAccMS.Repository;
import com.everis.SavingAccMS.Model.Owner;
import com.everis.SavingAccMS.Model.SavingAccount;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import reactor.core.publisher.Mono;
public interface SavingAccountRepo extends ReactiveMongoRepository<SavingAccount, String>
{
Mono<SavingAccount> findByNumber(String number);
//This one is the problem
Mono<SavingAccount> findByOwnerName(String name);
Mono<SavingAccount> findByOwner(Owner owner);
}
My Entity:
package com...SavingAccMS.Model;
import java.security.acl.Owner;
import javax.validation.constraints.NotBlank;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;
#Data
#Document(collection = "SavingAccs")
public class SavingAccount
{
#Id
private String id;
#NotBlank
private String number;
#NotBlank
private Owner owner;
#NotBlank
private String currency;
#NotBlank
private double balance = 0.00;
#NotBlank
private String status;
}
package com...SavingAccMS.Model;
import org.springframework.data.mongodb.core.mapping.Document;
import lombok.Data;
#Data
#Document(collection = "Owners")
public class Owner
{
public String dni;
public String name; //findBy this attribute is required.
}
According to your imports, the Owner in your SavingAccount refers to java.security.acl.Owner, not the one you defined yourself in om...SavingAccMS.Model.Owner. The former does not carry a name attribute.
I'm not certain but I don't think you can find the owner name in hibernate like that.
I would try the following!
#Query("SELECT * FROM SavingAccount where owner.name = :name")
Mono<SavingAccount> findByOwnerName(#Param("name") String name);

Resources