I have: A Request entity has a collection of Stages and each stage has a collection of StageItems. Stage items can be of several types and I use for them in JPA/Hibernate SINGLE_TABLE as inheritance strategy with a discriminator column.

I need my lucene query to return requests that have a particular detail in a stage item of type A (field aDetail in class StageItemA). I cannot get hibernate search to see the field aDetail in the subclass StageItemA.

So this lucene query does not work (returns 0 results):

stages.stageItems.aDetail:blah

But searching based on fields in StageItem works:

stages.stageItems.comment:yuppie

Using Luke, I can the fields from StageItem, like stages.stageItems.comment, but no field from StageItemA, like stages.stageItems.aDetail.

Entity definitions:

@Entity
@Table(name = "REQUEST")
@Indexed(index = "RequestIndex")
class Request implements Serializable {
    //...
    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
    @MapKeyColumn(name = "type", length = 50, nullable = false)
    @JoinTable(
            name = "REQUEST_STAGE",
            joinColumns = @JoinColumn(name = "REQUEST_ID", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "STAGE_ID", referencedColumnName = "id")
    )
    @IndexedEmbedded
    private Map<String, Stage> stages = new HashMap<>();
    //...
}

@Entity
@Table(name = "STAGE")
public class Stage implements Serializable {
    //...
    @OneToMany(fetch = FetchType.EAGER, targetEntity = StageItem.class, cascade = {
            CascadeType.ALL,
    }, orphanRemoval = true)
    @JoinTable(
            name = "STAGE_TO_STAGE_ITEM",
            joinColumns = @JoinColumn(name = "STAGE_ID"),
            inverseJoinColumns = @JoinColumn(name = "STAGE_ITEM_ID")
    )
    @Fetch(FetchMode.JOIN)
    @IndexedEmbedded
    private Set<StageItem> stageItems = new HashSet<>();
    //...
}

@Entity
@Table(name = "STAGE_ITEM")
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class StageItem implements Serializable {
    //...
    @Column(name = "TYPE")
    @Field
    protected String type;

    @Column(name="COMMENT")
    @Field
    protected String comment;
    //...
}

@Entity
@DiscriminatorValue(value = "A")
public class StageItemA extends StageItem {
    //...
    @Column(name="A_DETAIL")
    @Field
    private String aDetail;
    //...
}

1 Answers

0
yrodiere On

@IndexedEmbedded only considers the declared type of your property, not the runtime type. So with you current mapping, @IndexedEmbedded will only index fields declared in class StageItem, not those declared in StageItemA.

There are plans to change that and take into account runtime polymorphism, but we're not quite there yet: it's not as obvious as it seems because of corner cases where we'd need to detect conflicts in the index schema between multiple subclasses. See HSEARCH-438 to track progress and find more information.

In the meantime, the easiest way out would be for you to declare a method and field in returning null in StageItem, and to override it in StageItemA to return the correct value.

Something like this:


@Entity
@Table(name = "STAGE_ITEM")
@DiscriminatorColumn(name = "TYPE", discriminatorType = DiscriminatorType.STRING)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class StageItem implements Serializable {
    //...
    @Column(name = "TYPE")
    @Field
    protected String type;

    @Column(name="COMMENT")
    @Field
    protected String comment;
    //...

    @Field(name = "aDetail")
    @javax.persistence.Transient
    protected String getADetailForHibernateSearch() {
        return null;
    }
}

@Entity
@DiscriminatorValue(value = "A")
public class StageItemA extends StageItem {
    //...
    @Column(name="A_DETAIL")
    private String aDetail;
    //...

    @Override
    protected String getADetailForHibernateSearch() {
        return aDetail;
    }
}

The main drawback is that indexing a transient method has negative impact on performance: in short, since Hibernate Search doesn't know where the data comes from, it will consider that any change to any property of a StageItem requires reindexing, whereas previously it would only have triggered reindexing when a relevant property was modified. You can try it though, the performance hit may not be that bad in your case.