7.5.2.5. 过滤器
Apache Lucene 具有强大的功能,允许您根据自定义过滤流程过滤查询结果。这是应用其他数据限制的一种非常强大的方式,特别是可以缓存和重复利用过滤器。使用案例包括:
- security
- 时序数据(例如,仅查看上个月的数据)
- 填充过滤器(例如,搜索仅限于给定类别)
Hibernate 搜索通过引入透明缓存的可参数名称过滤器的概念来进一步推动概念。对于熟悉 Hibernate 内核过滤器概念的用户,API 非常相似:
示例:为查询启用 Fulltext Filters
fullTextQuery = s.createFullTextQuery( query, Driver.class ); fullTextQuery.enableFullTextFilter("bestDriver"); fullTextQuery.enableFullTextFilter("security").setParameter( "login", "andre" ); fullTextQuery.list(); //returns only best drivers where andre has credentials
fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("bestDriver");
fullTextQuery.enableFullTextFilter("security").setParameter( "login", "andre" );
fullTextQuery.list(); //returns only best drivers where andre has credentials
在本例中,我们在查询之上启用了两个过滤器:您可以根据需要启用或禁用任意数量的过滤器。
声明过滤器是通过 @FullTextFilterDef 注释来完成的。此注释可以位于任何 @Indexed 实体上
,无论过滤器稍后要应用到哪一个查询。这意味着过滤定义是全局的,并且它们的名称必须唯一。如果定义了两个名称相同的注释,则会抛出 SearchException,以防有两个不同的 @FullTextFilterDef 注释。每个命名过滤器都必须指定其实际的过滤器实施。
示例:定义和实施过滤器
@FullTextFilterDefs( { @FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class), @FullTextFilterDef(name = "security", impl = SecurityFilterFactory.class) }) public class Driver { ... }
@FullTextFilterDefs( {
@FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class),
@FullTextFilterDef(name = "security", impl = SecurityFilterFactory.class)
})
public class Driver { ... }
BestDriversFilter 是一个简单的 Lucene 过滤器的例子,它可将结果集减少到分数为 5 的驱动程序。在本例中,指定的过滤器直接实施 org.apache.lucene.search.Filter
,并且包含 no-arg 构造器。
如果您的 Filter 创建需要额外的步骤,或者您想要使用的过滤器没有 no-arg 构造器,您可以使用工厂模式:
示例:使用 Factory Pattern 创建过滤器
Hibernate Search 将查找带注释的 @Factory
方法,并使用它来构建过滤器实例。工厂必须具有 no-arg 构造器。
Infinispan Query 使用 @Factory 注释的方法来构建过滤器实例。该工厂必须具有无参数构造器。
使用命名过滤器时,参数必须传递到过滤器。例如,安全过滤器可能想要了解您要应用哪个安全级别:
示例:将参数传递给定义的过滤器
fullTextQuery = s.createFullTextQuery( query, Driver.class ); fullTextQuery.enableFullTextFilter("security").setParameter( "level", 5 );
fullTextQuery = s.createFullTextQuery( query, Driver.class );
fullTextQuery.enableFullTextFilter("security").setParameter( "level", 5 );
每一参数名称应对过滤器或过滤目标命名过滤器定义的工厂关联集合器。
示例:在实际过滤器实施中使用参数
注意注释为 @Key 的方法返回 FilterKey 对象。返回的对象具有特殊合同:密钥对象必须实现等效的()/ hashCode(),这样,只有在给定 Filter 类型相同并且参数集相同时,两个键才相等。换句话说,两个过滤器键是相等的,只有在可以交换从中生成键的过滤器时才适用。密钥对象用作缓存机制中的密钥。
只有在以下情况下才需要 @key 方法:
- 过滤器缓存系统被启用(默认启用)
- 过滤器有参数
在大多数情况下,使用 StandardFilterKey
实现会足够好。它将等号()/ hashCode()实现委派到每个参数等于和 hashcode 方法。
如前文所述,定义的过滤器按默认设置进行缓存,缓存使用硬链接和软引用组合来允许在需要时处理内存。硬引用缓存跟踪最近使用的过滤器,并在需要时转换最不用于 SoftReferences 的过滤器。达到硬引用缓存的限制后,其他过滤器将缓存为 SoftReferences。要调整硬引用缓存的大小,请使用 hibernate.search.filter.cache_strategy.size
(默认为 128)。如需高级使用过滤器缓存,请实现您自己的 FilterCachingStrategy。classname 由 hibernate.search.filter.cache_strategy
定义。
此过滤器缓存机制不应与缓存实际过滤器结果混淆。在 Lucene 中,通常的做法是使用 IndexReader 围绕 CachingWrapperFilter 包装过滤器。打包程序将缓存 getDocIdSet(IndexReader reader)
方法返回的 DocIdSet,以避免昂贵的重新输入。务必要提及,计算的 DocIdSet 仅适用于同一 IndexReader 实例,因为读取器在打开时有效地代表索引的状态。打开的 IndexReader 中无法更改文档列表。但是,不同的/新的 IndexReader 实例可能会在另一组文档中正常工作(可以是来自不同的索引,还是仅仅因为索引已更改),因此必须重新计算缓存的 DocIdSet。
Hibernate 搜索也有助于实现缓存的这一方面。默认情况下,@FullTextFilterDef 的 缓存
标志被设置为 FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS
,它将自动缓存过滤器实例,并将指定的过滤器围绕 CachingWrapperFilter 的 Hibernate 实施包装。与此类 SoftReferences 的 Lucene 版本和硬参考计数一起使用(请参阅关于过滤缓存)。可以使用 hibernate.search.filter.cache_docidresults.size
(默认为 5)来调整硬引用计数。可以使用 @FullTextFilterDef.cache
参数控制嵌套行为。这个参数有三个不同的值:
值 | 定义 |
---|---|
FilterCacheModeType.NONE | 没有过滤实例,Hibernate Search 也不会缓存任何结果。对于每个过滤器调用,都会创建一个新的过滤器实例。此设置对于快速更改数据集或大量内存受限环境可能有用。 |
FilterCacheModeType.INSTANCE_ONLY | 过滤器实例在并发 Filter.getDocIdSet()调用之间缓存和重复利用。DocIdSet 结果不会被缓存。当过滤器使用自己的特定缓存机制或过滤器结果因为应用程序特定的事件在这两种情况下都不需要造成 DocIdSet 缓存而动态更改时,此设置很有用。 |
FilterCacheModeType.INSTANCE_AND_DOCIDSETRESULTS | 过滤器实例和 DocIdSet 结果都会被缓存。这是默认值。 |
在以下情况下,应缓存过滤器:
- 系统不会经常更新目标实体索引(换句话说,IndexReader 被大量重用)
- Filter 的 DocIdSet 计算成本高昂(与执行查询的时间相对相对)