I am trying to port to HS 6.0 from 5.11 and have run into the following issue. Previously, in HS 5.11 I did the following to retrieve the score for each search result.
FullTextQuery fullTextQuery = createFullTextQuery(criteria);
fullTextQuery.setProjection(FullTextQuery.SCORE, FullTextQuery.THIS, FullTextQuery.DOCUMENT_ID);
fullTextQuery.setFirstResult((int)pageable.getOffset());
fullTextQuery.setMaxResults(pageable.getPageSize());
//FullTextQuery will log if org.hibernate.search.fulltext_query is set to DEBUG
projResultList = fullTextQuery.getResultList();
//getResultSize must go after getResultList or query will be executed twice
resultSize = fullTextQuery.getResultSize();;
Iterator projIter = projResultList.iterator();
int docId;
float score;
PatentDocDO doc;
while (projIter.hasNext()) {
projResult = (Object[]) projIter.next();
docRes = new PatentDocSearchResultDO();
score = (float) projEntry[0];
doc = (PatentDocDO) projEntry[1];
docId=(int) projResult[2];
docRes.setDoc(doc);
docRes.setRelevanceScore(score);
docRes.setDocId(docId);
results.add(docRes);
}
Note that everything is done in a single query. As my search queries are quite complex, that is important from a performance perspective. In digging through the documentation of HS 6, I cannot find the equivalent of this in the query/projection DSL. I see the projection section which just returns the scores, but that requires a second search. Am I missing something? Is there a way in HS 6 to do this in a single query?
Thanks,
Keith
PS> Yes, my users are advanced patent searchers and understand the concepts and challenges around scoring.
SearchResult<PatentDocSearchResultDO> searchResult = Search.session(entityManager)
.search(PatentDocDO.class)
.select(f -> f.composite(
(score, entity) -> {
PatentDocSearchResultDO hit = new PatentDocSearchResultDO();
hit.setDoc(entity);
hit.setRelevanceScore(score);
},
f.score(), f.entity()))
.where(f -> {
// Use your "criteria" here, and return the resulting predicate
})
.fetch((int)pageable.getOffset(), pageable.getPageSize());
results = searchResult.hits();
resultSize = searchResult.total().hitCount();
Or, better, if you declare an appropriate constructor in PatentDocSearchResultDO:
SearchResult<PatentDocSearchResultDO> searchResult = Search.session(entityManager)
.search(PatentDocDO.class)
.select(f -> f.composite(PatentDocSearchResultDO::new, f.score(), f.entity()))
.where(f -> {
// Use your "criteria" here, and return the resulting predicate
})
.fetch((int)pageable.getOffset(), pageable.getPageSize());
results = searchResult.hits();
resultSize = searchResult.total().hitCount();
The only thing missing here is the projection to a Lucene “docId” (which is a long). This feature was removed for a good reason: the docId can change from one query to another, so it’s not a reliable identifier outside of the context of a single query execution. Wherever Hibernate Search used to expect a Lucene “docId”, it now expects an entity identifier.
If you’re interested in some identifier, just for uniqueness, you can use the entity identifier (whatever you mapped with @Id or @DocumentId in your domain model) or the Hibernate Search document identifier (which is a String).
See entity reference projection and document reference projection. They both return an object with an id() method that returns an identifier.
I did see the composite section, but I misinterpreted it. I thought because the search was specifying a specific class (in this case PatentDocDO.class) that I could only retrieve class-associated projectable fields for the projection. Regarding the doc id, I no longer need that. I was pulling it previously in order to run explain, but that approach is no longer needed. Thank you very much.