Hibernate 搜索:使用过滤器对嵌套对象进行排序,如何?

Hibernate search : Sorting with filter on nested object, how to?

我必须编写休眠搜索查询代码(用于弹性搜索数据库后端),其中包括此类条件排序:

Date dateOfBirth = new Date('01/01/2000');
Integer age = 10;
if (dateOfBirth == null) {
   //then sort by age
}
else {
   //sort by date of birth
}

我在 Hibernate Search Reference 中找到了一个示例来编写这种条件排序代码,它可以像这样完成(引用示例):

List<Author> hits = searchSession.search( Author.class )
.where( f -> f.matchAll() )
.sort( f -> f.field( "books.pageCount" )
.mode( SortMode.AVG )
.filter( pf -> pf.match().field( "books.genre" )
.matching( Genre.CRIME_FICTION ) ) )
.fetchHits( 20 );

我的问题是休眠搜索在运行时抛出异常。我的排序过滤器代码:

 case DATE_SIGNATURE:
                FieldSortOptionsStep bivSortFirst = f.field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)
                        .filter(fa ->
                                {
                                    PredicateFinalStep a = fa.bool(bo -> bo.must(fa.exists().field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)));
                                    return fa.bool(b0 -> b0.must(a));
                                }
                        );
                FieldSortOptionsStep bivSortSecond = f.field(Depot_.VENTE + "." + Vente_.ACTE + "." + Acte_.SIGNATURE)
                        .filter(fa ->
                                {
                                    PredicateFinalStep a = fa.bool(bo -> bo.mustNot(fa.exists().field(Depot_.VENTE + "." + Vente_.DATE_SIGNATURE)));
                                    PredicateFinalStep b = fa.bool(bo -> bo.must(fa.exists().field(Depot_.VENTE + "." + Vente_.ACTE + "." + Acte_.SIGNATURE)));
                                    return fa.bool(b0 -> b0.must(a).must(b));
                                }
                        );
                sortFieldOrderedList.add(bivSortFirst);
                sortFieldOrderedList.add(bivSortSecond);
                break;

在上面的示例中,我按优先级对两个字段进行了排序。第一个可同化为 'date of birth',第二个可同化为 'age'。在运行时,过滤器不被休眠搜索接受,然后抛出如下异常:

错误信息:

HSEARCH400604: Invalid sort filter: field 'vente.acte.signature' is not contained in a nested object. Sort filters are only available if the field to sort on is contained in a nested object. Context: field 'vente.acte.signature'

我阅读这样做,我需要去 'inner_hits' 查询弹性搜索。但是我如何使用休眠搜索来做到这一点 API ?

谢谢。

编辑:类 的 Hibernate 映射:

@Entity
@Indexed
public class Depot {
    ...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "vente_fk")
    protected Vente vente;
                
    @IndexedEmbedded(includePaths = {
    Vente_.ID,
    Vente_.DATE_SIGNATURE,
    Vente_.DATE_SIGNATURE_ACTE,
    Vente_.ACTE + "." + Acte_.SIGNATURE,
        and much more
    }
    public Vente getVente() {
            return this.vente;
        }
    ...
}

@Entity
public class Vente {

    @OneToMany(mappedBy = Depot_.VENTE, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    protected Set<Depot> depot = new HashSet<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acte_fk")
    protected Acte acte;
...
    @AssociationInverseSide(inversePath = @ObjectPath(@PropertyValue(propertyName = Acte_.VENTE)))
    @IndexedEmbedded
    public Acte getActe() {
        return this.acte;
    }
...
}

@Entity
public class Acte {
...
    @GenericField(projectable = Projectable.YES, sortable = Sortable.YES, aggregable = Aggregable.YES)
    protected Date signature;
    
    @OneToMany(mappedBy = Vente_.ACTE)
    protected Set<Vente> vente = new HashSet<>();
    
    public Date getSignature() {
        return this.signature;
    }
...
}

据我所知,对于每个 Depot,最多有一个 Acte 和一个 Vente。所以你想做的有点奇怪,因为排序过滤通常用于多值嵌套对象。

它不起作用的原因是您没有将 @IndexedEmbedded 对象(vente、acte)标记为“嵌套”;如文档中所述,过滤仅适用于 nested objects. And "nested" has a very precise meaning,它与“索引嵌入”不是同义词。

但是,我认为在这种情况下整个方法是错误的:您不应该使用过滤。我敢肯定,即使您将 @IndexedEmbedded 对象标记为“嵌套”,您也会面临其他问题,因为您尝试做的并不是过滤的预期目的。其中一个问题可能是性能;嵌套文档意味着运行时连接,而运行时连接并不便宜。

相反,请考虑在编制索引时解决此问题。不要在搜索时试图找出每个文档使用哪个日期,而是在索引时这样做:

@Entity
@Indexed
public class Depot {
    //...
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "vente_fk")
    protected Vente vente;
                
    @IndexedEmbedded(includePaths = {
    Vente_.ID,
    Vente_.DATE_FOR_SORT, // <================= ADD THIS
    Vente_.DATE_SIGNATURE,
    Vente_.DATE_SIGNATURE_ACTE,
    Vente_.ACTE + "." + Acte_.SIGNATURE,
        //and much more
    })
    public Vente getVente() {
            return this.vente;
        }

}

@Entity
public class Vente {

    @OneToMany(mappedBy = Depot_.VENTE, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    protected Set<Depot> depot = new HashSet<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "acte_fk")
    protected Acte acte;
//...
    @AssociationInverseSide(inversePath = @ObjectPath(@PropertyValue(propertyName = Acte_.VENTE)))
    @IndexedEmbedded
    public Acte getActe() {
        return this.acte;
    }

    // v================= ADD THIS
    @Transient
    @IndexingDependency(derivedFrom = {
        @ObjectPath(@PropertyValue(propertyName = Vente_.DATE_SIGNATURE)),
        @ObjectPath(@PropertyValue(propertyName = Vente_.ACTE), @PropertyValue(propertyName = Acte_.SIGNATURE)),
    })
    public Date getDateForSort() {
        if ( getDateSignature() != null ) {
            return getDateSignature();
        }
        else {
            return getActe().getSignature();
        }
    }
    // ^================= ADD THIS
//...
}