What is the best way to save jena Result set in database? - spring

I am creating a Spring web application that queries SPARQL endpoints. As a requirement, I'm supposed to save the query and the result for later viewing and editing. So far I have created some entities (QueryInfo, Result, Endpoint) that I use to save the information entered about the Query and the Result. However I'm having trouble with saving the actual results themselves
public static List<String> getSelectQueryResult(QueryInfo queryInfo){
Endpoint endpoint = queryInfo.getEndpoint();
Query query = QueryFactory.create(queryInfo.getContent());
List<String> subjectStrings = query.getResultVars();
List<String> list = new ArrayList();
RDFConnection conn = RDFConnectionFactory.connect(endpoint.getUrl());
QueryExecution qExec = conn.query(queryInfo.getContent()) ; //SELECT DISTINCT ?s where { [] a ?s } LIMIT 100
ResultSet rs = qExec.execSelect() ;
while (rs.hasNext()) {
QuerySolution qs = rs.next();
System.out.println("qs: "+qs);
RDFNode rn = qs.get(subjectStrings.get(0)) ;
System.out.print(qs.varNames());
if(rn!= null) {
if (rn.isLiteral()) {
Literal literal = qs.getLiteral(subjectStrings.get(0));
list.add(literal.toString());
} else if (rn.isURIResource()) {
Resource subject = qs.getResource(subjectStrings.get(0));
System.out.println("Subject: " + subject.toString());
list.add(subject.toString());
}
}
}
return list;
}
My Result entity looks like this:
#Entity #Data #Table(schema = "sparql_tool") public class Result {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 10485760)
private String content;
#OneToOne
#JoinColumn(name = "query_info_id",referencedColumnName = "id")
private QueryInfo queryInfo;
#Column(length = 10485760)
#Convert(converter = StringListConverter.class)
private List<String> contentList;
public Result() {
}
public Result(String content, QueryInfo queryInfo, List<String> list) {
this.content = content;
this.queryInfo = queryInfo;
this.contentList=list;
}
}
I used to save the actual results in the List contentList attribute. However, this only works when the query has only one result variable. If I have multiple result variables I have a table instead of a list. What is the best way to save this result in DB?
I'm working with an SQL DB if that is relevant. Thank you so much in advance!

Related

Spring specification search inside JSON array of String

I am using Postgres version 12 database and ORM Hibernate mapping, and Assuming that i have next Entity:
#Entity
public class MyEntity {
#Type(type = "json")
#Column(name = "names", columnDefinition = "json")
private List<String> names;
// Getters and Setters
}
So the data will be persisted like that inside the table column (names):
["Sara", "Anton", "Lars"]
and i want to add a specification to search on that json from postgres database, i tried next one but it didn't work :(
private static Specification<MyEntity> withNames(String name) {
return (root, query, builder) -> {
Expression<String> function = builder.function("json_array_elements", String.class, root.get("names"));
return StringUtils.isBlank(name) ? builder.conjunction() : builder.like(function, name + "%");
};
}
Any suggestions of how to get it work?

Hibernate search : Sorting with filter on nested object, how to?

I have to code an hibernate search query (for elastic search database backend) which include a conditionnal sort of this kind :
Date dateOfBirth = new Date('01/01/2000');
Integer age = 10;
if (dateOfBirth == null) {
//then sort by age
}
else {
//sort by date of birth
}
I found an example to code this conditionnal sort inside Hibernate Search Reference, it can be done like this (quoted example) :
List<Author> hits = searchSession.search( Author.class )
.where( f -> f.matchAll() )
.sort( f -> f.field( "books.pageCount" )
.mode( SortMode.AVG )
.filter( pf -> pf.match().field( "books.genre" )
.matching( Genre.CRIME_FICTION ) ) )
.fetchHits( 20 );
My problem is that I hibernate search throws an exception at runtime. My sort filter code :
case DATE_SIGNATURE:
FieldSortOptionsStep bivSortFirst = f.field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)
.filter(fa ->
{
PredicateFinalStep a = fa.bool(bo -> bo.must(fa.exists().field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)));
return fa.bool(b0 -> b0.must(a));
}
);
FieldSortOptionsStep bivSortSecond = f.field(Depot_.VENTE + "." + Vente_.ACTE + "." + Acte_.SIGNATURE)
.filter(fa ->
{
PredicateFinalStep a = fa.bool(bo -> bo.mustNot(fa.exists().field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)));
PredicateFinalStep b = fa.bool(bo -> bo.must(fa.exists().field(Depot_.VENTE + "." + Vente_.ACTE + "." + Acte_.SIGNATURE)));
return fa.bool(b0 -> b0.must(a).must(b));
}
);
sortFieldOrderedList.add(bivSortFirst);
sortFieldOrderedList.add(bivSortSecond);
break;
In the above example, I sort on two fields by priority. The first is assimilable to 'date of birth' and the second to 'age'. At runtime, the filter are not accepted by hibernate search and then throws an exception like follows :
The error message :
HSEARCH400604: Invalid sort filter: field 'vente.acte.signature' is
not contained in a nested object. Sort filters are only available if
the field to sort on is contained in a nested object. Context: field
'vente.acte.signature'
I read to do so, I need to go for 'inner_hits' query for elastic search. But how do I do this with hibernate search API ?
Thanks.
EDIT : Hibernate mapping of classes :
#Entity
#Indexed
public class Depot {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vente_fk")
protected Vente vente;
#IndexedEmbedded(includePaths = {
Vente_.ID,
Vente_.DATE_SIGNATURE,
Vente_.DATE_SIGNATURE_ACTE,
Vente_.ACTE + "." + Acte_.SIGNATURE,
and much more
}
public Vente getVente() {
return this.vente;
}
...
}
#Entity
public class Vente {
#OneToMany(mappedBy = Depot_.VENTE, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected Set<Depot> depot = new HashSet<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "acte_fk")
protected Acte acte;
...
#AssociationInverseSide(inversePath = #ObjectPath(#PropertyValue(propertyName = Acte_.VENTE)))
#IndexedEmbedded
public Acte getActe() {
return this.acte;
}
...
}
#Entity
public class Acte {
...
#GenericField(projectable = Projectable.YES, sortable = Sortable.YES, aggregable = Aggregable.YES)
protected Date signature;
#OneToMany(mappedBy = Vente_.ACTE)
protected Set<Vente> vente = new HashSet<>();
public Date getSignature() {
return this.signature;
}
...
}
From what I can see, for each Depot, there is at most one Acte and one Vente. So what you're trying to do is a bit exotic, as filtering in sorts is generally used on multi-valued nested objects.
The reason it's not working is you didn't mark the #IndexedEmbedded objects (vente, acte) as "nested"; as explained in the documentation, filtering only works on nested objects. And "nested" has a very precise meaning, it's not synonmymous with "indexed-embedded".
However, I think the whole approach is wrong in this case: you shouldn't use filtering. I'm quite sure that even if you mark the #IndexedEmbedded objects as "nested", you will face other problems, because what you're trying to do isn't the intended purpose of filtering. One of those problems could be performance; nested documents mean runtime joins, and runtime joins aren't cheap.
Instead, consider solving this problem at indexing time. Instead of trying to figure out which date to use for each document when searching, do that when indexing:
#Entity
#Indexed
public class Depot {
//...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "vente_fk")
protected Vente vente;
#IndexedEmbedded(includePaths = {
Vente_.ID,
Vente_.DATE_FOR_SORT, // <================= ADD THIS
Vente_.DATE_SIGNATURE,
Vente_.DATE_SIGNATURE_ACTE,
Vente_.ACTE + "." + Acte_.SIGNATURE,
//and much more
})
public Vente getVente() {
return this.vente;
}
}
#Entity
public class Vente {
#OneToMany(mappedBy = Depot_.VENTE, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
protected Set<Depot> depot = new HashSet<>();
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "acte_fk")
protected Acte acte;
//...
#AssociationInverseSide(inversePath = #ObjectPath(#PropertyValue(propertyName = Acte_.VENTE)))
#IndexedEmbedded
public Acte getActe() {
return this.acte;
}
// v================= ADD THIS
#Transient
#IndexingDependency(derivedFrom = {
#ObjectPath(#PropertyValue(propertyName = Vente_.DATE_SIGNATURE)),
#ObjectPath(#PropertyValue(propertyName = Vente_.ACTE), #PropertyValue(propertyName = Acte_.SIGNATURE)),
})
public Date getDateForSort() {
if ( getDateSignature() != null ) {
return getDateSignature();
}
else {
return getActe().getSignature();
}
}
// ^================= ADD THIS
//...
}

JPA - How to copy and modify it's content of Page object?

I have this Meeting and Favorite models;
public class Meeting implements Serializable {
private long id;
private String meetingTitle;
private Date meetingStartDate;
private User host;
}
public class MeetingFavorite implements Serializable {
private long id;
private boolean active = false;
private Meeting meeting;
private Date updatedDate;
}
And I can successfully fetch MeetingFavorite page object like;
#GetMapping(value = "/favorite-meetings", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public ResponseEntity searchFavoriteMeetings(
MeetingFavoriteSpecification search, HttpSession session) {
Page<MeetingFavorite> page = meetingsService.findFavoriteMeetings(search);
return ResponseEntity.ok(page);
}
Is it possible to get Meeting contents only from MeetingFavorite Page w/ it's pagination data?
I tried this and it returns Meeting objects. But pagination data is lost.
Page<MeetingFavorite> page = meetingsService.findFavoriteMeetings(search);
List<Meeting> meetings = new ArrayList<Meeting>();
page.forEach(entity -> meetings.add(entity.getMeeting()));
final Page<Meeting> meetingPage = new PageImpl<>(meetings);
return ResponseEntity.ok(meetingPage);
Oh, I found the way. Thanks.
List<Meeting> meetings = new ArrayList<Meeting>();
page.forEach(entity -> meetings.add(entity.getMeeting()));
Sort sort = new Sort(Sort.Direction.DESC, "updatedDate");
Pageable pageable = new PageRequest(search.getOffset() / search.getLimit(), search.getLimit(), sort);
final Page<Meeting> meetingPage = new PageImpl<Meeting>(meetings, pageable, page.getTotalElements());
return ResponseEntity.ok(meetingPage);

SpringBootJpa in clause with subMatching

How to apply like with in clause in spring boot jpa.Below is the class.
#Table(name="media")
public class Media {
#NotBlank
private String url;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ElementCollection
private Set<String> tagList = new HashSet<String>();
public Media(String urlString) {
this.url = urlString ;
}
}
For example if there is a row with tagList ["mentos", "hurre"] and i want to search for "men" or ["men","hu"] this row should come ?
I have defined below method but it return a row only if string completely match.
Set<Media> findByTagListIn(List<String> tagList);
You need to query by specification like below:
//MediaRepository
import org.springframework.data.jpa.domain.Specification;
...
List<Media> findAll(Specification<Media> spec);
and create that specification in service class.
//MediaService
List<Media> findMediaByTags(List<String> tags){
Specification<Media> specification = (Specification<Media>) (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
for (String tag : tags) {
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.isMember(tag, root.get("tags")));
}
return predicate;
};
return mediaRepository.findAll(specification);
}

fetch all the data from main table and all the corresponding child record with activeYn != N., using spring jpa

Trying to achieve
fetch all the data from main table and all the corresponding child record with activeYn != N.
This is parent entity
#Entity
#Table(name="IR_TB_INCIDENT_HDR")
public class IncidentHdr implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="IR_TB_INCIDENT_HDR_INCIDENTID_GENERATOR", sequenceName="IR_SEQ_INCIDENT_ID",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="IR_TB_INCIDENT_HDR_INCIDENTID_GENERATOR")
#Column(name="INCIDENT_ID")
private long incidentId;
#Column(name="PATIENT_ID")
private Long patientId;
#OneToMany(cascade = {CascadeType.PERSIST,CascadeType.MERGE, CascadeType.REMOVE}, fetch = FetchType.LAZY, mappedBy="incidentHdr")
private Set<Attachments> attachments;
....
//other child entities
}
This is the child entity
#Entity
#Table(name="IR_TB_ATTACHMENTS")
public class Attachments implements Serializable {
private Long attachmentId;
private IncidentHdr incidentHdr;
private boolean activeYn;
}
Here we are genegating the custom query, we are appending only one condition here.
public IncidentHdr findIncidentDetailForId(Long incidentId) throws BusinessException {
StringBuilder query = null;
IncidentHdr incidentHdr = null;
StringBuilder incidentDetailQuery = null;
Query q = null;
Map < String, Object > parameters = new HashMap < String, Object > ();
List < String > criteria = new ArrayList < String > ();
try {
incidentDetailQuery = new StringBuilder();
query = new StringBuilder();
query.append(ReposJPQL.GET_INCIDENTS_DETAIL);
criteria.add("inc.incidentId = :incidentId ");
parameters.put("incidentId", incidentId);
if (criteria.size() > 0) {
for (int i = 0; i < criteria.size(); i++) {
incidentDetailQuery.append(" AND ");
incidentDetailQuery.append(criteria.get(i));
}
}
query.append(incidentDetailQuery);
q = em.createQuery(query.toString());
for (Entry < String, Object > entry: parameters.entrySet()) {
q.setParameter(entry.getKey(), entry.getValue());
}
incidentHdr = (IncidentHdr) q.getSingleResult();
}catch(IllegalArgumentException | IllegalStateException | DataAccessException | EntityNotFoundException e) {
logger.error(e.getMessage());
throw new BusinessException(e);
}
return incidentHdr;
}
ReposJPQL, Here defined the query with activeYn condition.
public interface ReposJPQL {
public String GET_INCIDENTS_DETAIL = "SELECT inc "
+ " FROM IncidentHdr inc left join inc.attachments att WHERE att.activeYn <> 'N' ";
}
Even though the records are present it return "org.springframework.dao.EmptyResultDataAccessException: No entity found for query; nested exception is javax.persistence.NoResultException: No entity found for query"
error
Or is there any other way to achieve this ? #Where(clause=...) option is pure hibernate so cant use that.

Resources