[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查询时可能的流程:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZG1fdmluY2VudA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
反映到代码中是这种:
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)
假设一个查询满足不了你的需求,那么你能够使用布尔查询将若干个查询结合起来。
以下是它的流程:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZG1fdmluY2VudA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
使用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索引来得到结果。
[Hibernate Search] (3) 基础查询的更多相关文章
- Hibernate search使用示例(基础小结-注解方式)
(对于项目环境配置,一直没怎么看过.这次经历里从基础环境搭建到hibernate search示例的完成) 1.首先创建project,选择了web project. 2.导入hibernate se ...
- 玩耍Hibernate系列(一)--基础知识
Hibernate框架介绍: Hibernate ORM 主要用于持久化对象(最常用的框架) Hibernate Search 用于对对象进行搜索,底层基于Apache Lucene做的 Hib ...
- hibernate search例子
[TOC] 1. 概念介绍 1.1. Hibernate Search Hibernate Search是Hibernate的子项目,把数据库全文检索能力引入到项目中,并通过"透明" ...
- Hibernate 中Criteria Query查询详解【转】
当查询数据时,人们往往需要设置查询条件.在SQL或HQL语句中,查询条件常常放在where子句中.此外,Hibernate还支持Criteria查询(Criteria Query),这种查询方式把查询 ...
- Spring Hibernate JPA 联表查询 复杂查询(转)
今天刷网,才发现: 1)如果想用hibernate注解,是不是一定会用到jpa的? 是.如果hibernate认为jpa的注解够用,就直接用.否则会弄一个自己的出来作为补充. 2)jpa和hibern ...
- Spring Hibernate JPA 联表查询 复杂查询
今天刷网,才发现: 1)如果想用hibernate注解,是不是一定会用到jpa的? 是.如果hibernate认为jpa的注解够用,就直接用.否则会弄一个自己的出来作为补充. 2)jpa和hibern ...
- [SpringBoot系列]--Spring Hibernate search 注解实现(未测试)
1.maven项目pom.xml加入依赖 <dependency> <groupId>org.hibernate</groupId> <artifactId& ...
- [转载]Hibernate如何提升数据库查询的性能
目录(?)[-] 数据库查询性能的提升也是涉及到开发中的各个阶段在开发中选用正确的查询方法无疑是最基础也最简单的 SQL语句的优化 使用正确的查询方法 使用正确的抓取策略 Hibernate的性能优化 ...
- 分享知识-快乐自己:Hibernate 中Criteria Query查询详解
1):Hibernate 中Criteria Query查询详解 当查询数据时,人们往往需要设置查询条件.在SQL或HQL语句中,查询条件常常放在where子句中. 此外,Hibernate还支持Cr ...
随机推荐
- RNN静态与动态
静态.多层RNN:import numpy as np import tensorflow as tf # 导入 MINST 数据集 from tensorflow.examples.tutorial ...
- Python 面向对象 特殊方法(魔法方法)
Python 的特殊方法,两边带双下划线的方法. 比如:__init__(self, ...).__del__(self) 1.__init__(self,...) : 构造方法 __init__(s ...
- Linux OOM-killer 内存不足时kill高内存进程的策略
OOM_killer是Linux自我保护的方式,当内存不足时不至于出现太严重问题,有点壮士断腕的意味 在kernel 2.6,内存不足将唤醒oom_killer,挑出/proc/<pid> ...
- spring boot 自动生成mybatis代码
1)在pom.xml中增加generator插件 <!--自动生成mybaits--> <plugin> <groupId>org.mybatis.generato ...
- C++关键字(保留字)
C++ 关键字 点击下表以进入具体释义 __abstract 2 __alignof Operator __asm __assume __based __box 2 __cdecl __declspe ...
- pwnable.kr cmd1之write up
看一下源代码: #include <stdio.h> #include <string.h> int filter(char* cmd){ ; r += strstr(cmd, ...
- 杭电 4907 Task schedule ·
Description 有一台机器,并且给你这台机器的工作表,工作表上有n个任务,机器在ti时间执行第i个任务,1秒即可完成1个任务. 有m个询问,每个询问有一个数字q,表示如果在q时间有一个工作表之 ...
- [第一波模拟\day1\T2]{分班}(divide.cpp)
[题目描述] 小N,小A,小T又大了一岁了. 现在,他们已经是高二年级的学生了.众所周知,高二的小朋友是要进行文理科分班考试的,这样子的话,三个好朋友说不定就会不分在一个班. 于是三个人决定,都考平均 ...
- C#基于引用创建单链表
在C语言,单链表的实现依赖指针,指针用来指向节点,那么,用C#实现,自然就想到引用,节点的引用不就类似于指向Node的指针嘛
- 可以从CSS框架中借鉴到什么
http://isux.tencent.com/css-framework.html http://isux.tencent.com/css-framework.html 现在很多人会使用 CSS 框 ...