Manchanda. P
Manchanda. P

Reputation: 321

Spring Boot Hibernate Search 6 Not Returning results for partial matches

I am implementing Hibernate Search 6 in my Spring Boot Application. I need the capability to search for partial matches, similar to %like% in SQL. From the documentation and other forums, it seems that Hibernate Search 6 has removed the explicit support of match and replaced it with the wild card queries.

However, I am not able to achieve the desired results i.e. %like%. Here's my relevant test bed code to reproduce the scenario:

Test Data:

phonenumber,area_zone,state
9871234567,DL,Delhi
9871234568,HR,Haryana
9871234569,PB,Punjab
9871234578,UP,Uttar Pradesh
9871234579,TS,Telangana
9871234479,AP,Andhra Pradesh

My DTO:

@Entity
@Table(name="PHONENUMBER", uniqueConstraints = {@UniqueConstraint(columnNames = "phonenumber")})
@Indexed
public class PhoneNumber {
    @Id
    @GeneratedValue
    private int id;

    //@FullTextField(searchAnalyzer = "custom_analyzer")
    @GenericField(sortable= Sortable.YES)
    private String phonenumber;
    
    @GenericField(sortable=Sortable.YES)
    private String area_zone;
    
    @GenericField(sortable= Sortable.YES)
    private String state;
}

Here's the implementation of my Custome JPA Repo for Hibernate Search:

public class CustomSearchRepositoryImpl implements CustomSearchRepository {

    @PersistenceContext
    private EntityManager em;

    //Hibernate Search 6
    @Override
    public List<PhoneNumber> search(String terms, int limit, int offset) {
        System.out.println(">>>>>searching for " + terms);

        return Search.session(em).search(PhoneNumber.class)
                .where(f -> f.match()
                        .fields("phonenumber", "area_zone", "state")
                        .matching(terms))
                .fetchHits(offset, limit);
    }
}

The search is triggered by the API: /search. The results are:

The search on 'full match' works fine but not for the partial match. Would need recommendations to make the partial search work.

PS: Spring Version is 2.6.7.

Upvotes: 0

Views: 40

Answers (2)

Nipuna Upeksha
Nipuna Upeksha

Reputation: 428

As mentioned in the Hibernate documentation (https://docs.jboss.org/hibernate/search/6.2/reference/en-US/html_single/#search-dsl-predicate-wildcard), you can use wildcards for your issue.

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.wildcard().field( "description" )
                .matching( "rob*t" ) )
        .fetchHits( 20 );

Looking at your implementation, you can edit it as shown below to get the wildcard results.

@Override
public List<PhoneNumber> search(String terms, int limit, int offset) {
    System.out.println(">>>>>searching for " + terms);

    return Search.session(em).search(PhoneNumber.class)
            .where(f -> f.wildcard()
                    .fields("phonenumber", "area_zone", "state")
                    .matching("*" + terms + "*"))  // Enables %LIKE% behavior
            .fetchHits(offset, limit);
}

Also you may want to use @FullTextField (https://docs.jboss.org/hibernate/search/6.2/reference/en-US/html_single/#mapping-directfieldmapping-annotations-fulltextfield) if you need to find partial matches. Refer to the examples in https://docs.jboss.org/hibernate/search/6.2/reference/en-US/html_single/#search-dsl-predicate-wildcard to get an idea about how to use it properly.

Upvotes: 0

mark_o
mark_o

Reputation: 2518

Since you are using generic fields, no analysis is applied to them, and as a result only exact matches work in that case, see the corresponding section of the documentation:

For most field types (number, date, …​), the match is exact. However, for full-text fields or normalized keyword fields, the value passed to the matching(…​) method is analyzed or normalized before being compared to the values in the index.

For wildcard predicates see this part of the documentation

List<Book> hits = searchSession.search( Book.class )
        .where( f -> f.wildcard().field( "description" )
                .matching( "rob*t" ) )
        .fetchHits( 20 );

if you want to be able to find partial matches with the match predicate the field should be annotated as @FullTextField and you should consider using ngram filter in your custom analyser applied to such field.

Upvotes: 1

Related Questions