Migrating Hibernate Search 5.11.11.Final to any Hibernate Search 6 - spring

Is there any way to avoid usage of BooleanJunction while migrating to Hibernate 6?
My (poor) understanding is that Hibernate 6 abandoned Quer DSL and is using JPA criteria instead. Code sample is just about 10 % of code that I have to cover all search cases and it uses more that shown here, meaning BooleanJunction is needed for this to work.
import javax.persistence.EntityManager;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.SearchFactory;
import org.hibernate.search.query.dsl.QueryContextBuilder;
import org.hibernate.search.query.dsl.EntityContext;
import org.hibernate.search.query.dsl.QueryBuilder;
// ...
#Autowired
private final EntityManager entityManager;
private static final Pattern SPACES =
Pattern.compile("\\s+", Pattern.UNICODE_CHARACTER_CLASS);
private static final int MIN_TOKEN_LENGTH = 2;
#AllArgsConstructor #Getter
public enum SearchField {
// there are 20 entries; here is one just to show
NAME(SearchField.NAME_STR, 20.0f);
private String fieldName;
private float boostFactor;
}
// ...
final FullTextEntityManager fullTextEntityManager =
Search.getFullTextEntityManager(this.entityManager);
final SearchFactory searchFactory =
fullTextEntityManager.getSearchFactory();
final QueryContextBuilder queryContextBuilder =
searchFactory.buildQueryBuilder();
final EntityContext entityContext =
queryContextBuilder.forEntity(clazz);
final QueryBuilder queryBuilder =
entityContext.get();
// ...
final String searchTerm = "some search term";
final String[] searchTerms =
Arrays.stream(SPACES.split(searchTerm))
.map(StringUtils::trimToNull)
.filter(Objects::nonNull)
.filter(s -> s.length() >= IndexingUtil.MIN_TOKEN_LENGTH)
.toArray(String[]::new);
final List<Query> queries = new LinkedList<>();
for (int i = 0; i < searchTerms.length; ++i) {
final String word = searchTerms[i];
queries.add(
queryBuilder
.bool()
.should(
QueryForField.q(
SearchField.NAME,
word,
queryBuilder
)
)
.boostedTo(SearchField.NAME.getBoostFactor()
);
);
}
// more queries are added, not only query by `name`
final BooleanJunction<?> master = queryBuilder.bool();
queries.stream().filter(Objects::nonNull).forEach(master::must);
final Query q = master.createQuery();

Related

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

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!

How does javax.persistence.criteria.CriteriaBuilder#or work?

I've created a custom repository to filter a list of entities from the db. Below is a basic initialization of the implemented class (entities are reduced to letters for clarification):
#Repository
public class CustomXRepositoryImpl implements CustomXRepository {
private final EntityManager em;
private final CriteriaBuilder builder;
private final CriteriaQuery<X> query;
private final Root<X> root;
#Autowired
public CustomXRepositoryImpl(EntityManager em) {
this.em = em;
builder = em.getCriteriaBuilder();
query = builder.createQuery(X.class);
root = query.from(X.class);
}
}
and the method I implemented:
#Override
public List<X> filter(FilterParams params) {
List<Predicate> restrictions = new ArrayList<>();
// some if-statements ...
if (params.getY() != null) {
Expression<?> path1 = root.get("a").get("b").get("y");
Expression<?> path2 = root.get("c").get("d").get("y");
Predicate p1 = builder.equal(path1, params.getY());
Predicate p2 = builder.equal(path2, params.getY());
// restrictions.add(p1); // 3 results for Y = 4
// restrictions.add(p2); // no result for Y = 4
restrictions.add(builder.or(p1, p2)); // no result
}
// more if-statements ...
query.where(restrictions.toArray(new Predicate[0]));
return em.createQuery(query).getResultList();
}
What makes me confused is the fact that builder.or(p1, p2) returns no result, even though p1 is satisfied. I also find the CriteriaBuilder#or documentation (Returns a disjunction of the given restriction predicates) very ambiguous.
Can anyone explain this behavior?

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.

Read Scheduler(Fixed-delay) from Database in Spring boot

I'm developing a spring boot project which uses spring batch scheduler to read data after 2 sec's and send it to the message broker (Activemq) which works fine with hardcoded fixed delay.
However, i'm now trying to read the #Scheduled(fixedDelay) from database rather hard coded but looks like nothing is working out. I can see the expression contains 10 secs but scheduler doesn't start
#Service
public class QuoteService implements ApplicationListener<BrokerAvailabilityEvent> {
private static Log logger = LogFactory.getLog(QuoteService.class);
private final MessageSendingOperations<String> messagingTemplate;
private final StockQuoteGenerator quoteGenerator = new StockQuoteGenerator();
private AtomicBoolean brokerAvailable = new AtomicBoolean();
private ReadCronExpressionDataService readCronExpressionDataService;
private int expression;
#Autowired
public QuoteService(MessageSendingOperations<String> messagingTemplate,ReadCronExpressionDataService readCronExpressionDataService) {
this.messagingTemplate = messagingTemplate;
this.readCronExpressionDataService=readCronExpressionDataService;
expression = readCronExpressionDataService.readData();
}
#Scheduled(fixedDelay=expression) //#Scheduled(fixedDelay=2000)
public void sendQuotes() {
for (Quote quote : this.quoteGenerator.generateQuotes()) {
if (logger.isTraceEnabled()) {
logger.trace("Sending quote " + quote);
}
if (this.brokerAvailable.get()) {
this.messagingTemplate.convertAndSend("/topic/price.stock." + quote.getTicker(), quote);
}
}
}
private static class StockQuoteGenerator {
private static final MathContext mathContext = new MathContext(2);
private final Random random = new Random();
private final Map<String, String> prices = new ConcurrentHashMap<>();
public StockQuoteGenerator() {
this.prices.put("CTXS", "24.30");
this.prices.put("DELL", "13.03");
this.prices.put("EMC", "24.13");
this.prices.put("GOOG", "893.49");
this.prices.put("MSFT", "34.21");
this.prices.put("ORCL", "31.22");
this.prices.put("RHT", "48.30");
this.prices.put("VMW", "66.98");
}
public Set<Quote> generateQuotes() {
Set<Quote> quotes = new HashSet<>();
for (String ticker : this.prices.keySet()) {
BigDecimal price = getPrice(ticker);
quotes.add(new Quote(ticker, price));
}
return quotes;
}
private BigDecimal getPrice(String ticker) {
BigDecimal seedPrice = new BigDecimal(this.prices.get(ticker), mathContext);
double range = seedPrice.multiply(new BigDecimal(0.02)).doubleValue();
BigDecimal priceChange = new BigDecimal(String.valueOf(this.random.nextDouble() * range), mathContext);
return seedPrice.add(priceChange);
}
}
}
Any idea?

Resources