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
//...
}
我必须编写休眠搜索查询代码(用于弹性搜索数据库后端),其中包括此类条件排序:
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
//...
}