在本文中,我们将讨论Hibernate Search的基础知识,如何配置它,并且我们将实现一些简单的查询。
有些时候我们必须实现全文搜索功能,使用我们已经熟悉的工具总是优点。
如果我们已经将Hibernate和JPA用于ORM,那么我们距离Hibernate Search只有一步之遥。
Hibernate Search集成了Apache Lucene,这是一种用Java编写的高性能和可扩展的全文搜索引擎库。这将Lucene的强大功能与Hibernate和JPA的简单性相结合。
简而言之,我们只需要向我们的域类添加一些额外的注释,并且该工具将处理数据库/索引同步等问题。
Hibernate Search还提供了Elasticsearch集成; 然而,由于它仍处于实验阶段,我们将在这里重点介绍Lucene。
我们首先需要将必要的依赖添加到我们的pom.xml中:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-search-orm</artifactId>
<version>5.8.2.Final</version>
</dependency>
为了简单起见,我们将使用H2作为我们的数据库:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
我们还必须指定Lucene应该存储索引的位置。
这可以通过属性 hibernate.search.default.directory_provider完成。
我们将选择文件系统,这对我们的用例来说是最直接的选择。官方文档中列出了更多选项 。 对于群集应用程序来说,文件系统主 / 文件系统 - 从属和 无限大是值得注意的,其中索引必须在节点之间同步。
我们还必须定义索引将存储在其中的默认基本目录:
hibernate.search.default.directory_provider = filesystem
hibernate.search.default.indexBase = /data/index/default
配置完成后,我们现在准备指定我们的模型。
在JPA注解@Entity和@Table之上,我们必须添加 @Indexed注释。它告诉Hibernate Search实体产品将被索引。
之后,我们必须通过添加@Field注释来将所需的属性定义为可搜索的:
@Entity
@Indexed
@Table(name = "product")
public class Product {
@Id
private int id;
@Field(termVector = TermVector.YES)
private String productName;
@Field(termVector = TermVector.YES)
private String description;
@Field
private int memory;
// getters, setters, and constructors
}
该termVector = TermVector.YES 属性将需要“更多类似这样的”查询后。
在开始实际查询之前,我们必须首先触发Lucene建立索引:
FullTextEntityManager fullTextEntityManager
= Search.getFullTextEntityManager(entityManager);
fullTextEntityManager.createIndexer().startAndWait();
在初始构建之后,Hibernate Search会负责保持索引是最新的。I. e。我们可以像往常一样通过EntityManager创建,操作和删除实体。
注意:我们必须确保实体完全致力于数据库,然后才能被Lucene发现并编入索引(顺便说一下,这也是为什么我们的示例代码测试用例中的初始测试数据导入来自专用的JUnit测试用例,用@Commit注解)。
现在,我们准备创建我们的第一个查询。
在下一节中,我们将展示准备和执行查询的一般工作流程。
之后,我们将为最重要的查询类型创建一些示例查询。
一般来说准备和执行查询由四个步骤组成:
在第1步中,我们必须得到一个JPA FullTextEntityManager,并从中获得一个QueryBuilder:
FullTextEntityManager fullTextEntityManager
= Search.getFullTextEntityManager(entityManager);
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder()
.forEntity(Product.class)
.get();
在第2步中,我们将通过Hibernate查
在第2步中,我们将通过Hibernate查询DSL创建一个Lucene查询:
org.apache.lucene.search.Query query = queryBuilder
.keyword()
.onField("productName")
.matching("iphone")
.createQuery();
在第3步中,我们将Lucene查询包装到一个Hibernate查询中:
org.hibernate.search.jpa.FullTextQuery jpaQuery
= fullTextEntityManager.createFullTextQuery(query, Product.class);
最后,在第4步中,我们将执行查询:
List<Product> results = jpaQuery.getResultList();
注意:默认情况下,Lucene按照相关性排序结果。
步骤1,3和4对于所有查询类型都是相同的。
在下文中,我们将重点关注第2步,即如何创建不同类型的查询。
最基本的用例是搜索一个特定的单词。
这就是我们在前一节中已经做过的事情:
Query keywordQuery = queryBuilder
.keyword()
.onField("productName")
.matching("iphone")
.createQuery();
在这里,keyword()指定我们正在寻找一个特定的单词,onField()告诉Lucene在哪里寻找和匹配()寻找什么。
模糊查询的工作方式与关键字查询相似,不同之处在于我们可以定义“模糊性”的限制,超过此限制,Lucene将接受这两个词作为匹配。
通过withEditDistanceUpTo(),我们可以定义一个术语可能与另一个术语相差多少。它可以设置为0,1和2,默认值是2(注意:这个限制来自Lucene的实现)。
通过 withPrefixLength(),我们可以定义前缀的长度,模糊性应该忽略它:
Query fuzzyQuery = queryBuilder
.keyword()
.fuzzy()
.withEditDistanceUpTo(2)
.withPrefixLength(0)
.onField("productName")
.matching("iPhaen")
.createQuery();
Hibernate Search还使我们能够执行通配符查询,即查询某个单词的一部分未知。
为此,我们可以对单个字符使用“ ?”,对任何字符序列使用“ *”:
Query wildcardQuery = queryBuilder
.keyword()
.wildcard()
.onField("productName")
.matching("Z*")
.createQuery();
如果我们想要搜索多个单词,我们可以使用短语查询。如果需要,我们可以使用phrase()和withSlop()来查找确切的或近似的句子。斜率因子定义了句子中允许的其他单词的数量:
Query phraseQuery = queryBuilder
.phrase()
.withSlop(1)
.onField("description")
.sentence("with wireless charging")
.createQuery();
使用先前的查询类型,我们必须明确指定查询类型。
如果我们想给用户更多的权力,我们可以使用简单的查询字符串查询:由此,他可以在运行时定义自己的查询。
支持以下查询类型:
以下示例将结合模糊,短语和布尔查询:
Query simpleQueryStringQuery = queryBuilder
.simpleQueryString()
.onFields("productName", "description")
.matching("Aple~2 + \"iPhone X\" + (256 | 128)")
.createQuery();
范围查询搜索 给定边界之间的值。这可以应用于数字,日期,时间戳和字符串:
Query rangeQuery = queryBuilder
.range()
.onField("memory")
.from(64).to(256)
.createQuery();
我们最后的查询类型是“ 更像这个 ” - 查询。为此,我们提供了一个实体,Hibernate Search返回一个具有相似实体的列表,每个实体都具有相似性分数。
如前所述,在这种情况下,我们的模型类中的 TermVector = TermVector.YES属性是必需的:它告诉Lucene在索引期间存储每个术语的频率。
基于此,相似性将在查询执行时计算:
Query moreLikeThisQuery = queryBuilder
.moreLikeThis()
.comparingField("productName").boostedTo(10f)
.andField("description").boostedTo(1f)
.toEntity(entity)
.createQuery();
List<Object[]> results = (List<Object[]>) fullTextEntityManager
.createFullTextQuery(moreLikeThisQuery, Product.class)
.setProjection(ProjectionConstants.THIS, ProjectionConstants.SCORE)
.getResultList();
到目前为止,我们只使用onField()创建了查询一个属性的查询。
根据用例,我们还可以搜索两个或更多属性:
Query luceneQuery = queryBuilder
.keyword()
.onFields("productName", "description")
.matching(text)
.createQuery();
此外,我们可以指定要分别搜索的每个属性,例如,如果我们要为一个属性定义增强:
Query moreLikeThisQuery = queryBuilder
.moreLikeThis()
.comparingField("productName").boostedTo(10f)
.andField("description").boostedTo(1f)
.toEntity(entity)
.createQuery();
最后,Hibernate Search还支持使用各种策略来组合查询:
聚合类似于布尔逻辑的AND,OR和NOT。但是,名称不同,强调它们对相关性也有影响。
例如,两个查询之间的SHOULD类似于布尔OR:如果其中一个查询有匹配,则返回该匹配。
但是,如果两个查询匹配,则与只有一个查询匹配的情况相比,匹配的关联性会更高:
Query combinedQuery = queryBuilder
.bool()
.must(queryBuilder.keyword()
.onField("productName").matching("apple")
.createQuery())
.must(queryBuilder.range()
.onField("memory").from(64).to(256)
.createQuery())
.should(queryBuilder.phrase()
.onField("description").sentence("face id")
.createQuery())
.must(queryBuilder.keyword()
.onField("productName").matching("samsung")
.createQuery())
.not()
.createQuery();
在本文中,我们讨论了Hibernate Search的基础知识,并展示了如何实现最重要的查询类型。更高级的功能可以在官方文档中找到 。
https://www.leftso.com/article/391.html