[Hibernate Search] (6) 高级查询 - 过滤,投影和分面

本文介绍了如何使用Hibernate Search进行高级查询,包括如何使用过滤器进行动态筛选,通过投影获取所需属性,实现分面搜索,以及查询时的提升和时间限制。过滤器允许在不与数据库交互的情况下,利用Lucene动态筛选结果。投影减少了数据库交互,提高性能。分面搜索则提供了基于离散值和连续值的分类方式,便于用户筛选。同时,文章还讨论了查询时间限制,确保查询性能。
摘要由CSDN通过智能技术生成

高级查询

在介绍了更多的高级映射功能之后,是时候回顾一下之前介绍过的查询功能了,看看如何借助这些高级的映射功能来使用一些高级的查询功能。本文会通过以下几个方面进行介绍:

  • 如何在不和数据库进行任何交互的前提下,借助Lucene的力量来动态的筛选结果
  • 如何通过使用基于投影(Projection)的查询来获取需要的属性,从而避免与数据库的交互
  • 如何使用分面搜索(Faceted Search)对搜索结果进行划分
  • 如何使用查询时提升(Boosting)
  • 如何给查询设置时间限制

过滤(Filtering)

虽然是全文搜索,但是我们有时候需要将搜索的结果限定到某个范围内。比如,当我们只需要搜索特定设备上的支持的App,有以下几个思路:

  • 将限定范围作为搜索关键字传入到查询对象中。但是稍微想想就会发现问题:这样做只会增大搜索的范围而导致更多的结果被返回,因为搜索关键字变多了。

  • 使用布尔查询,向其中添加must子查询。这样做是可行的,只不过这样做会让DSL难以维护,失去其简洁的特点。同时,如果需要过滤逻辑相对比较复杂的话,使用DSL会让代码变的臃肿。

  • 由于Hibernate Search中的FullTextQuery是继承自Hibernate ORM Query(或者相应的JPA Query)对象。所以我们可以考虑使用类似ResultTransformer这种对象进行过滤。但是这样做的问题是会让代码和数据库之间的交互变的更多,导致性能的下滑。

实际上,针对这一类问题Hibernate Search提供了一套更优雅和高效的解决方案:过滤器(Filter)。

过滤器会将过滤的逻辑封装到其中,然后在运行时通过动态地使用这些过滤器来完成需要的过滤操作。过滤行为是针对Lucene索引的,被过滤的内容绝对不会出现在最终的搜索结果中。因此从某种意义上而言,它也减小了最终需要从数据库中获取的数据量。

创建一个过滤器工厂

过滤器对应着Lucene中的org.apache.lucene.search.Filter类型。因此,对于简单的过滤器直接创建Filter类型的一个子类就够了。但是,如果想在运行时根据条件动态地生成Filter实例,就需要使用过滤器工厂:

public class DeviceFilterFactory {
    private String deviceName;

    @Factory
    public Filter getFilter() {
        PhraseQuery query = new PhraseQuery();
        StringTokenizertokenzier = new StringTokenizer(deviceName);
        while(tokenzier.hasMoreTokens()) {
            Term term = new Term("supportedDevices.name", tokenzier.nextToken());
            query.add(term);
        }
        Filter filter = new QueryWrapperFilter(query);
        return new CachingWrapperFilter(filter);
    }

    public void setDeviceName(String deviceName) {
        this.deviceName = deviceName.toLowerCase();
    }
}

上述代码中最关键的就是@Factory注解的使用。它表明了getFilter方法能够返回一个过滤器实例。在getFilter的实现中,必须要使用一些Lucene的原生API,它虽然没有Hibernate Search DSL方便,但是也并不难理解。

最终返回的过滤器的类型时CachingWrapperFilter,使用它是为了将过滤器进行缓存来避免创建不必要的重复Filter,它封装了QueryWrapperFilter实例,而后者则建立在一个Query对象上。这个Query对象表示的就是进行筛选操作所必要的查询。这里我们想精确的匹配设备名称,因此使用的查询类型时短语查询(PhraseQuery)。

让我们回顾一下数据在被Lucene索引时所经历的过程:

  • 解析器会进行字符过滤,分词和词条过滤,然后将每个词条都抽象为Lucene中的一个数据单元(即Term类型,上面的代码中有用到)。
  • 在将数据写入索引前,默认的解析器会将字符串数据转换为小写的形式。

但是在使用Hibernate Search时,这些Lucene细节都不需要开发人员费心。可是,当像上述代码那样使用底层Lucene API时,就需要注意这些细节了。因此,在setDeviceName方法中,我们会将传入的deviceName转换为小写的。然后在创建Query类型时,会将分词得到的每个词条都先转换为Term类型,再添加到Query中。

添加过滤器键(Filter Key)

正因为在创建过滤器时,我们使用了CachingWrapperFilter完成了一次封装用来缓存该过滤器。所以当需要从缓存中取回某个过滤器时,我们还需要使用一个Key,这个Key就是所谓的过滤器键(Filter Key)。这里我们使用需要过滤的设备名称作为键,配合@Key实现如下:

@Key
Public FilterKey getKey() {
    DeviceFilterKey key = new DeviceFilterKey();
    key.setDeviceName(this.deviceName);
    return key;
}

该方法也实现

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值