【问题标题】:Hibernate search : Sorting with filter on nested object, how to?休眠搜索:使用过滤器对嵌套对象进行排序,如何?
【发布时间】:2021-10-04 10:43:05
【问题描述】:

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

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;

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

错误信息:

HSEARCH400604:排序过滤器无效:字段“vente.acte.signature”为 不包含在嵌套对象中。排序过滤器仅在以下情况下可用 要排序的字段包含在嵌套对象中。上下文:字段 'vente.acte.signature'

我是这样做的,我需要使用“inner_hits”查询来进行弹性搜索。但是如何使用休眠搜索 API 做到这一点?

谢谢。

编辑:类的休眠映射:

@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;
    }
...
}

【问题讨论】:

  • 您需要展示您的模型,以便人们了解您要做什么。特别是:VenteActe,以及任何超类或超接口。
  • 当然还有Depot
  • Depot 是根文档(@Indexed)。 Vente 是@indexEmbedded。 Acte 是@IndexEmbedded。索引文档看起来像 json (_source 是 Depot): _source { vente: { acte: { signature: { } } } 是否足够?
  • 恐怕不是。我需要查看实际的注释属性,例如Depot#venteVente#acte等。
  • ok 添加到编辑部分。

标签: elasticsearch hibernate-search


【解决方案1】:

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

它不起作用的原因是您没有将 @IndexedEmbedded 对象 (vente, acte) 标记为“嵌套”;如文档中所述,过滤仅适用于nested objects。并且“嵌套”有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
//...
}

【讨论】:

  • 好答案。我已经按照你说的做了:删除这个不起作用的过滤器并预先计算这个动态排序值(但代价是保持这个索引值是最新的)。感谢您对嵌套对象的建议,我不知道这个概念。但是弹性搜索存储其文档的方式告诉我,我将我的内容索引为嵌套而不是扁平化:文档是一棵 json 树,而不仅仅是 _source {} 下的属性。我的字段被提到像嵌套只有@IndexEmbedded:Field1 +“。” + 字段 2 + "." + ....等
  • 即使在 Elasticsearch 中,默认情况下索引内容也是展平的。您的 JSON 是否显示为嵌套并不重要:索引的结构不同,默认情况下它是展平的。请参阅nested datatype(选择加入)和object datatype(默认设置)。
猜你喜欢
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-13
  • 1970-01-01
  • 2019-06-25
相关资源
最近更新 更多