[Hibernate Search] (3) 基础查询

基础查询

眼下我们仅仅用到了基于keyword的查询,实际上Hibenrate Search DSL还提供了其他的查询方式,以下我们就来一探到底。

映射API和查询API

对于映射API。我们能够通过使用Hibernate提供的注解来完毕映射工作。同一时候我们也能够使用JPA提供的注解来完毕。类似的,对于查询API,我们也能够从Hibernate和JPA提供的查询API中进行选择。

每种方式都有它的长处和缺点,比方当我们使用Hibernate提供的查询API时,意味着能够使用很多其它的特性,毕竟Hibernate Search就是建立在Hibernate之上的。而当我们选择JPA的查询API时,意味着应用能够更方便的切换ORM的实现。比方我们想将Hibernate替换成EclipseLink。

Hibernate
Search DSL

所谓的Hibernate Search DSL,实际上就是用于编写查询代码的一些列API:

import org.hibernate.search.query.dsl.QueryBuilder;

// ...

String searchString = request.getParameter("searchString");
QueryBuilder queryBuilder = fullTextSession.getSearchFactory()
    .buildQueryBuilder().forEntity( App.class ).get();
org.apache.lucene.search.Query luceneQuery = queryBuilder
    .keyword()
    .onFields("name", "description")
    .matching(searchString)
    .createQuery();

它採用链式编程的方式将查询中关键的部分封装成一个个方法进行连续调用。当下,非常多API都被设计成这样。比方jQuery的API。以及Java 8中最新的Stream类型的API等。同一时候,一些设计模式如建造者模式也大量地使用了这样的技术。

关键字查询(Keyword
Query)

基于keyword的查询。是最为主要的一种查询方式。眼下见到的样例都是基于keyword查询的。 为了运行这样的查询,第一步是得到一个QueryBuilder对象,而且说明须要查询的目标实体:

QueryBuilder queryBuilder = fullTextSession.getSearchFactory().buildQueryBuilder()
    .forEntity(App.class).get();

下图反映了在创建keyword查询时可能的流程:

反映到代码中是这种:

org.apache.lucene.search.Query luceneQuery = queryBuilder
    .keyword()
    .onFields("name", "description", "supportedDevices.name", "customerReviews.comments")
    .matching(searchString)
    .createQuery();

onFields方法能够看做是多个onField方法的组合,为了方便一次性地声明全部查询域。 假设onFields中接受的某个域在相应实体的索引中不存在相关信息,那么查询会报错。所以,须要确保传入到onFields方法中的域确实是存在于实体的索引中的。

对于matching方法,通常而言它须要接受的是一个字符串对象,表示查询的keyword。可是实际上借助FieldBridge,传入到该方法的參数能够是随意类型。在“高级映射”一文中会对FieldBridge进行介绍。

对于传入的keyword字符串,它或许包括了多个keyword(使用空白字符分隔,就像我们使用搜索引擎时)。Hibernate Search会默认地将它们切割成一个个的keyword,然后逐个进行搜索。

终于,createQuery方法会结束DSL的定义并返回一个Lucene查询对象。最后,我们能够通过FullTextSession(Hibernate)或者FullTextEntityManager(JPA)来得到终于的Hibernate Search查询对象(FullTextQuery):

FullTextQuery hibernateQuery =
    fullTextSession.createFullTextQuery(luceneQuery, App.class);

模糊查询(Fuzzy
Query)

当我们使用搜索引擎时,它都可以非常“聪明”地对一些输入错误进行更正。而在Hibernate Search中,我们也可以通过模糊查询来让查询更加智能。

当使用了模糊查询后,当keyword和目标字串之间的匹配程度低于设置的某个阈值时,Hibernate Search也会觉得匹配成功而返回结果。

这个阈值的范围在0和1之间:0代表不论什么字串都算匹配,而1则代表仅仅有全然符合才算匹配。

所以当这个阈值取了0和1之间的某个值时,就代表查询可以支持某种程度的模糊。

当使用Hibernate Search DSL来定义模糊查询时。可能的流程例如以下:

它一開始使用的也是keyword方法来定义一个基于keyword的查询,毕竟模糊查询也仅仅是keyword查询的一种。 它在最后也会使用onField/onFields来指定查询的目标字段。

仅仅只是在keyword和onField/onFields方法中间会定义模糊查询的相关參数。

fuzzy方法会使用0.5作为模糊程度的默认值,越接近0就越模糊,越接近1就越精确。因此。这个值是一个折中的值,在多种环境中都可以通用。

假设不想使用该默认值,还能够通过调用withThreshold方法来指定一个阈值:

luceneQuery = queryBuilder
    .keyword()
    .fuzzy()
    .withThreshold(0.7f)
    .onFields("name", "description", "supportedDevices.name", "customerReviews.comments")
    .matching(searchString)
    .createQuery();

除了withThreshold方法外。还能够使用withPrefixLength方法来指定每一个词语中,前多少个字符须要被排除在模糊计算中。

通配符查询(Wildcard
Query)

在通配符查询中,问号(?

)会被当做一个随意字符。而星号(*)则会被当做零个或者多个字符。

在Hibernate Search DSL中使用通配符搜索的流程例如以下:

须要使用wildcard方法来指定它是一个支持通配符的查询。

精确短语查询(Exact
Phrase Query)

前面提到过。Hibernate Search会在运行查询前将keyword使用空白字符进行切割,然后对得到的词语逐个查询。

然而,有时候我们须要查询的就是一个完整的短语,不须要Hibernate Search多此一举。在搜索引擎中。我们通过使用双引號来表示这样的情况。

在Hibernate Search DSL中,能够通过短语查询来完毕,一下是流程图:

sentence方法接受的參数必须是一个String类型,这一点和matching有所不同。

withSlop方法接受一个整型变量作为參数,它提供了一种原始的模糊查询方式:短语中额外能够出现的词语数量。比方我们要查询的是“Hello World”,那么在使用withSlop(1)后,“Hello Big World”也会被匹配。

那么在详细的代码中,我们能够首先进行推断,假设搜索字符串被引號包括了。那么就使用短语查询:

if(isQuoted(searchString)) {
    luceneQuery = queryBuilder
        .phrase()
        .onField("name")
        .andField("description")
        .andField("supportedDevices.name")
        .andField("customerReviews.comments")
        .sentence(searchStringWithQuotesRemoved)
        .createQuery();
}

范围查询(Range
Query)

范围查询的流程:

顾名思义,范围查询通过给定上限值和下限值来对某些域进行的查询。

因此。日期类型和数值类型一般会作为此类查询的目标域。

above。below方法用来单独指定下限值和上限值。而from和to方法必须成对使用。 它们能够结合excludeLimit来将区间从闭区间转换为开区间:

比方from(5).to(10).excludeLimit()所代表的区间就是:5 <= x < 10。

以下是一个查询拥有4星及以上评价的App实体:

luceneQuery = queryBuilder
    .range()
    .onField("customerReviews.stars")
    .above(3).excludeLimit()
    .createQuery();

布尔(组合)查询(Boolean(Combination)
Query)

假设一个查询满足不了你的需求,那么你能够使用布尔查询将若干个查询结合起来。

以下是它的流程:

使用bool方法来表明这个查询是一个组合查询,会组合多个子查询。

它至少须要包括一个must子查询或者一个should查询。

must和should分别表示的是逻辑与(Logical-AND)和逻辑或(Logical-OR)的语义。

一般。不要同一时候使用must和should,由于这会让should中的查询毫无意义。仅仅有在须要依据相关度对结果的排序进行调整时,才会将must和should联合使用。

比方。下述代码用来查询支持设备xPhone而且拥有5星评价的App实体:

luceneQuery = queryBuilder
    .bool()
    .must(
        queryBuilder
            .keyword()
            .onField("supportedDevices.name")
            .matching("xphone")
            .createQuery()
    )
    .must(
        queryBuilder
            .range()
            .onField("customerReviews.stars")
            .above(5)
            .createQuery()
    )
    .createQuery();

排序(Sorting)

默认情况下,查询结果应该依照其和查询条件间的相关度进行排序。关于相关度排序,会在兴许的文章中介绍。

可是我们也能够不再使用相关度作为排序的根据,转而我们能够使用日期,数值类型甚至字符串的顺序作为排序根据。

比方,对App的搜索结果。我们能够使用其名字在字母表中的顺序进行排序。

为了支持对于某个域的排序。我们须要向索引中加入一些必要的信息。在对字符串类型的域进行索引时,默认的分析器会将该域的值进行分词,所以对于某个值“Hello World”,在索引中会有两个入口对“Hello”和“World”进行单独保存。这样做可以让查询更具效率,可是当我们须要对该域进行排序时,分词器是不须要的。

因此,我们能够对该域设置两个@Field注解:

@Column
@Fields({
    @Field,
    @Field(name="sorting_name", analyze=Analyze.NO)
})
private String name;

一个用来建立标准的索引,一个用来建立用于排序的索引,当中指定了analyze=Analyze.NO,默认情况下分词器是被使用的。

这个域就能够被用来创建Lucene的SortField对象,并集合FullTextQuery使用:

import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;

// ...

Sort sort = new Sort(new SortField("sorting_name", SortField.STRING));
hibernateQuery.setSort(sort); // a FullTextQuery object

运行此查询后,得到的结果会依照App名字,从A-Z进行排序。 实际上。SortField还可以接受第三个boolean类型的參数,当传入true时,排序结果会被颠倒即从Z-A。

分页(Pagination)

当搜索会返回大量结果时,通常都不可能将它们一次性返回。而是使用分页技术一次仅仅返回并显示一部分数据。

对于Hibernate Search的FullTextQuery对象。能够使用例如以下代码完毕分页:

hibernateQuery.setFirstResult(10);
hibernateQuery.setMaxResults(5);
List<App> apps = hibernateQuery.list();

setFirstResult指定的是偏移量。它一般是通过 页码(从0開始) * 一页中的记录数 计算得到。

比方以上代码中的10实际上就是 2 * 5,因此它透露出来的信息是:显示第3页的5条数据。

而为了得到查询的结果数量,能够通过getResultSize方法获得:

int resultSize = hibernateQuery.getResultSize();

在使用getResultSize方法时,不涉及到不论什么的数据库操作。它只通过Lucene索引来得到结果。

时间: 2024-11-10 00:14:18

[Hibernate Search] (3) 基础查询的相关文章

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

高级查询 在介绍了更多的高级映射功能之后,是时候回顾一下之前介绍过的查询功能了,看看如何借助这些高级的映射功能来使用一些高级的查询功能.本文会通过以下几个方面进行介绍: 如何在不和数据库进行任何交互的前提下,借助Lucene的力量来动态的筛选结果 如何通过使用基于投影(Projection)的查询来获取需要的属性,从而避免与数据库的交互 如何使用分面搜索(Faceted Search)对搜索结果进行划分 如何使用查询时提升(Boosting) 如何给查询设置时间限制 过滤(Filtering)

[Hibernate Search] 初识Hibernate Search

初识Hibernate Search 要让你的应用具备Hibernate Search赋予的全文搜索的能力,需要做以下三件事: 给项目添加必要的依赖和配置信息 给你的实体类添加必要的信息,从而让Lucene知道如何对它们进行索引(Indexing) 在需要的地方使用符合Hibernate Search规范的查询来完成业务逻辑 对于需要添加的依赖信息,会在以后进行介绍.我们首先来看看代码该如何写. 我们会使用一个类似于经典的"Java Pet Store"那样的Web应用来展示Hiber

【Hibernate步步为营】--hql查询之实体对象查询

上篇文章简单介绍了hql它作为Hibernate的查询语言,封装了基本上SQL的所有查询操作,HQL能够实现对数据库文件的增删改查,该篇文章着重讨论HQL实体对象的查询方法. 一.实体对象查询 实体对象查询是hql查询的基础,作为一种对象查询语言,在查询操作时和sql不同,查询字符串中的内容要使用类名和类的属性名来代替.这种查询方法相对简单,只要有SQL功底,使用hql是很简单的,但是有一些问题需要注意,就是查询获取数据不是目的,需要考虑的是如何编写出高效的查询语句,这才是讨论的重点. 1.1

Hibernate 中Criteria Query查询详解【转】

当查询数据时,人们往往需要设置查询条件.在SQL或HQL语句中,查询条件常常放在where子句中.此外,Hibernate还支持Criteria查询(Criteria Query),这种查询方式把查询条件封装为一个Criteria对象.在实际应用中,使用Session的createCriteria()方法构建一个org.hibernate.Criteria实例,然后把具体的查询条件通过Criteria的add()方法加入到Criteria实例中.这样,程序员可以不使用SQL甚至HQL的情况下进行

[Hibernate Search] 实体类型的映射

实体类型映射 在上一篇文章中,简要介绍了如何使用Hibernate Search来对一个实体进行全文搜索. 然而,在真实的应用中,实体与实体之间的关系也许更为复杂.为了对复杂实体进行搜索,就需要让底层的Lucene查询也能够理解这些关系. 下图反映了Database,Hibernate,Hibernate Search和Lucene之间的关系: 域映射选项(Field Mapping Options) 我们已经知道@Field注解用来让某个域可以被全文搜索到. 实际上,在添加该注解后,Hiber

Spring整合hibernate(1)之基础整合

Spring整合hibernate3之基础整合 Spring集成hibernate3和4有一定的区别,目前基本都在使用3,所以此处内容以3为基础: 1.导入hibernate的包和Spring的包 1.1.导入Spring的依赖包 1.2.导入Log4j的依赖包:log4j-1.2.16.jar 1.3.导入dbcp的依赖包:commons-dbcp-1.4.jar.commons-pool-1.5.6.jar 1.4.导入hibernate3的依赖包 hibernate全部版本地址:http:

Hibernate关系映射基础

1.  Hibernate关系映射基础 1.1.  Doctype <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 指定根元素和dtd文件的命名空间. 1.2.  hibernate-mapping <hiberna

[Hibernate Search] (5) 解析和条件索引

解析(Analysis) 当一个实体域被Lucene索引时,往往还会经历一个语法分析(Parsing)和转换(Conversion)的步骤,这些步骤被称为解析.在前文中,我们提到过Hibernate Search会默认对字符串类型的实体域进行分词,而这个分词过程就需要用到解析器(Analyzer).在需要对实体域进行排序的场合,需要禁用这个默认的分词行为. 在解析过程中,还可以借助Apache Solr提供的组件来完成更多的操作.为了弄清楚Solr组件是如何参与到这个过程中并完成更多的操作,需要

Spring Hibernate JPA 联表查询 复杂查询

(转自:http://www.cnblogs.com/jiangxiaoyaoblog/p/5635152.html) 今天刷网,才发现: 1)如果想用hibernate注解,是不是一定会用到jpa的? 是.如果hibernate认为jpa的注解够用,就直接用.否则会弄一个自己的出来作为补充. 2)jpa和hibernate都提供了Entity,我们应该用哪个,还是说可以两个一起用? Hibernate的Entity是继承了jpa的,所以如果觉得jpa的不够用,直接使用hibernate的即可