These types are not indexed, nor is any of their subtypes

Viewed 51

I am using hibernate search and Lucene for searching the problem is this I have a user class that is a superclass that is indexed properly (I can make searches) but I also have a doctor class that is a sub-class of the medical class that is an also a sub-class of the employee class so it's like this doctor->(extends)->medicalstaff->(extends)->employee->(extends)->user the problem is when you search in user class it works properly but the result is all the user records that match the name in the database I don't need this I need a way to limit hibernate to only search for doctors or nurses...etc code
User class

package com.gradproject.hmis.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gradproject.hmis.enums.BloodType;
import com.gradproject.hmis.enums.MaritalStatus;
import com.gradproject.hmis.utils.BloodTypeConverter;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import net.bytebuddy.utility.RandomString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;


import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@Setter
@Getter
@Entity
@Table(name = "users")
@NoArgsConstructor
@Indexed
@Inheritance(strategy = InheritanceType.JOINED)
public class User {
    protected User(String email, String phoneNumber) {
        this.email = email;
        this.phoneNumber = phoneNumber;
    }

    @Id
    @SequenceGenerator(
            name = "user_sequence",
            sequenceName = "user_sequence"
    )
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
    private Long id;
    @FullTextField
    @Column(name = "first_name", length = 150)
    private String firstName;
    @FullTextField
    @Column(name = "last_name", length = 150)
    private String lastName;

    //@NotNull
    @Column(name = "national_id", length = 14, unique = true)
    private Long nationalId;
    @NotEmpty
    @Email(regexp = "^[A-Za-z0-9._]+@[A-Za-z0-9._-]+\\.[A-Za-z]{2,6}$",
            message = "Enter a valid email address"
    )
    @FullTextField
    @Column(length = 125, unique = true)
    private String email;
    @FullTextField
    @NotEmpty
    @Pattern(regexp = "^[0-9]{11}", message = "Enter a valid phone number")
    @Column(name = "phone_number", unique = true)
    private String phoneNumber;

    @Column
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private String password;

    @Column
    private Boolean gender;

    @Column(name = "birth_date")
    private LocalDate birthDate;

    @Convert(converter = BloodTypeConverter.class)
    @Column(length = 3)
    private BloodType bloodType;

    @Enumerated(EnumType.STRING)
    @Column(name = "marital_status", length = 20)
    private MaritalStatus maritalStatus;
    @IndexedEmbedded
    @Column
    private FileInfo image;

    @JsonIgnore
    @Column(name = "jwt_token_key")
    private String JwtTokenKey = RandomString.make(12);

    @Column(name = "is_active")
    private Boolean isActive = true;

    @CreationTimestamp
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Address> addresses = new HashSet<>();

    @ManyToMany(cascade = CascadeType.ALL)
    private Set<Permission> permissions = new HashSet<>();

    public Boolean hasPermission(String code) {
        return permissions.stream().filter(p -> code.equals(p.getCode())).count() > 0;
    }

    @JsonProperty(value = "age", access = JsonProperty.Access.READ_ONLY)
    public Integer getAge() {
        if (birthDate == null) {
            return null;
        }
        return (int) ChronoUnit.YEARS.between(birthDate, LocalDate.now());
    }

    @JsonProperty(value = "full_name", access = JsonProperty.Access.READ_ONLY)
    public String getFullName() {
        if (firstName != null || lastName != null)
            return String.format("%s %s", firstName, lastName);
        return email;
    }


    @Override
    public String toString() {
        return getFullName();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User user = (User) o;
        return id != null && Objects.equals(id, user.id);
    }

    @Override
    public int hashCode() {
        return getClass().hashCode();
    }
}
 

employee class


package com.gradproject.hmis.model;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

@Setter
@Getter
@Entity
@NoArgsConstructor
@Inheritance(strategy = InheritanceType.JOINED)
@Indexed
public class Employee extends User {
    public Employee(String email, String phoneNumber, 
        BigDecimal salary, Department department, Job job, Boolean isAdmin) {
        super(email, phoneNumber);
        this.salary = salary;
        this.department = department;
        this.job = job;
        this.isAdmin = isAdmin;
    }

    //@NotNull
    private BigDecimal salary;

    @ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id", 
        foreignKey = @ForeignKey(name="DEPARTMENT_ID_FK"))
    private Department department;

    //@NotNull
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "job_id", 
        foreignKey = @ForeignKey(name="JOB_ID_FK"))
    private Job job;

    private Boolean isAdmin = false;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Shift> shifts = new ArrayList<>();

}

medical staff class


package com.gradproject.hmis.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.gradproject.hmis.enums.MedicalStaffType;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Set;


@Setter
@Getter
@Entity
@NoArgsConstructor
@PrimaryKeyJoinColumn(name = "employee_id")
@JsonIgnoreProperties({"hibernate_lazy_initializer"})
@Indexed
public class MedicalStaff extends Employee {
    public MedicalStaff(MedicalStaffType staffType){
        this.staffType = staffType;
    }

    public MedicalStaff(String email, String phoneNumber, 
            BigDecimal salary, Boolean isAdmin, Department department,
            Job job, String role, MedicalStaffType staffType) {
        super(email, phoneNumber, salary, department, job, isAdmin);
        this.role = role;
        this.staffType = staffType;
    }

    @ElementCollection(targetClass=String.class)
    private Set<String> specialties = new HashSet<>();

    //@NotEmpty
    private String role;

    @ManyToOne(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
    @JoinColumn(name = "clinic_id")
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    private Clinic clinic;

    private String description;
    @Column(name="max_patient_number")
    private Integer maxPatientNumber = 3;

    @Column(name = "appointment_price")
    private BigDecimal appointmentPrice;
    @Enumerated(EnumType.ORDINAL)
    @Column(name="staff_type")
    private MedicalStaffType staffType;
}

and doctor class

package com.gradproject.hmis.model;

import com.gradproject.hmis.enums.MedicalStaffType;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import java.math.BigDecimal;
@Indexed
public class Doctor extends MedicalStaff {

    public Doctor() {
        super(MedicalStaffType.DoctorType);
    }

    public Doctor(String email, String phoneNumber, BigDecimal salary, Boolean isAdmin,
    Department department, Job job, String role) {
        super(email, phoneNumber, salary, isAdmin, 
        department, job, role, MedicalStaffType.DoctorType);
    }
}

my search method

 @Override
    public List<User> searchforUsers(String term, int start, int size) {
        SearchResult<User> userresult = Search.session(entityManager)
                .search(User.class)
                .where(f -> f.match()
                        .fields("firstName", "lastName", "email")
                        .matching(term).fuzzy()).fetch(start, size);
        return userresult.hits();
    }

this method works properly but when I do the same for doctor's class

   @Override
    public List<Doctor> searchForDoctors(String term, int start, int size) {

        SearchResult<Doctor> supplierresults = Search.session(entityManager)
                .search(Doctor.class)
                .where(f -> f.match()
                        .fields("firstName", "lastName", "email")
                        .matching(term).fuzzy()).fetch(start, size);

        return supplierresults.hits();
    }

I get this error


Invalid target types: [com.gradproject.hmis.model.Doctor] These types are not indexed, nor is any of their subtypes

the whole error is


 org.hibernate.search.util.common.SearchException: HSEARCH000234: Invalid target types: [com.gradproject.hmis.model.Doctor] These types are not indexed, nor is any of their subtypes.
    at org.hibernate.search.mapper.pojo.scope.impl.PojoScopeDelegateImpl.create(PojoScopeDelegateImpl.java:76) ~[hibernate-search-mapper-pojo-base-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.pojo.mapping.impl.PojoMappingDelegateImpl.createPojoScope(PojoMappingDelegateImpl.java:87) ~[hibernate-search-mapper-pojo-base-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.mapping.impl.HibernateOrmMapping.doCreateScope(HibernateOrmMapping.java:435) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.mapping.impl.HibernateOrmMapping.createScope(HibernateOrmMapping.java:376) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSession.scope(HibernateOrmSearchSession.java:164) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSession.massIndexer(HibernateOrmSearchSession.java:158) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.session.impl.DelegatingSearchSession.massIndexer(DelegatingSearchSession.java:69) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at org.hibernate.search.mapper.orm.session.SearchSession.massIndexer(SearchSession.java:142) ~[hibernate-search-mapper-orm-6.1.5.Final.jar:6.1.5.Final]
    at com.gradproject.hmis.OnApplicationStartEvent.onApplicationEvent(OnApplicationStartEvent.java:54) ~[classes/:na]
    at com.gradproject.hmis.OnApplicationStartEvent.onApplicationEvent(OnApplicationStartEvent.java:19) ~[classes/:na]
    at com.gradproject.hmis.OnApplicationStartEvent$$FastClassBySpringCGLIB$$df5c6fda.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:783) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:753) ~[spring-aop-5.3.16.jar:5.3.16]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:698) ~[spring-aop-5.3.16.jar:5.3.16]
    at com.gradproject.hmis.OnApplicationStartEvent$$EnhancerBySpringCGLIB$$8b5686d8.onApplicationEvent(<generated>) ~[classes/:na]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.16.jar:5.3.16]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.16.jar:5.3.16]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.16.jar:5.3.16]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.16.jar:5.3.16]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.16.jar:5.3.16]
    at org.springframework.boot.context.event.EventPublishingRunListener.ready(EventPublishingRunListener.java:114) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$ready$6(SpringApplicationRunListeners.java:82) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplicationRunListeners$$Lambda$1502/0x000000009fb302a8.accept(Unknown Source) ~[na:na]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplicationRunListeners.ready(SpringApplicationRunListeners.java:82) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.4.jar:2.6.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.4.jar:2.6.4]
    at com.gradproject.hmis.HmisApplication.main(HmisApplication.java:28) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-2.6.4.jar:2.6.4]
1 Answers

I don't know what you Doctor class is, but it's evidently not a Hibernate ORM entity. There's is no @Entity annotation on that class.

The Hibernate ORM mapper for Hibernate Search (the one you're using) only indexes Hibernate ORM entities. So, currently, your @Indexed annotation on Doctor is being ignored.

I'd suggest annotating Doctor with @Entity?


(I filed a ticket on JIRA, HSEARCH-4344, to automatically warn about this configuration problem early. It will be tricky to implement, but hopefully someday we'll spare someone the confusion you're experiencing right now).

Related