在 最近的 Hibernate Search 发布公告 中,您可能已经注意到了关于简单查询字符串的一些内容。
文档仍然比较简略,我们希望在这个功能最终定稿之前,给它更多的关注并收集一些反馈。
此功能是已经发布的 5.8.0.Beta1 版本的一部分,因此您现在就可以尝试使用它(无论是使用 Lucene 后端还是新的 Elasticsearch 后端)。
什么是简单查询字符串?
让我们从一些历史开始。
Lucene 4.7.0 引入了一个新的查询解析器,即 SimpleQueryParser
,该解析器被描述为“用于人类输入查询的解析器”。该解析器的目的是成为一个非常简单且宽容的状态机,用于解析最终用户输入的查询。
该解析器能够将 keyword "some phrase" -keywordidontwant fuzzy~ prefix*
转换为 Lucene 查询,给用户带来更多功能(短语查询、模糊查询、布尔运算符……)。宽容的部分很重要,因为它会尝试构建最佳可能的查询,即使查询不是我们认为是语法正确的。
另一个优点是它可以搜索多个字段。您基本上与 Lucene 建立以下合约
-
用户将输入一个搜索查询(大致上是语法正确的)
-
它将在您指定的字段上搜索(您也可以为每个字段指定特定的权重)
-
您可以启用您想要向用户公开的每个功能(例如,您可以启用短语查询,但不启用布尔运算符)
-
构建查询不会抛出异常
那么,对我们来说简单的查询字符串是什么?就是遵循 SimpleQueryParser
语法的字符串,或者说,用户在搜索框中输入的内容。
让我们举一个例子
下面,我们将基于以下 Book
实体进行讨论
@Entity
@Indexed
public class Book {
// [...]
@Field
private String title;
@Field
private String summary;
@Field
private String author;
// [...]
}
我们的目标是能够在 title
字段中搜索 war peace
,并给予 5
的权重,在 summary
字段中搜索 2
的权重,并且只返回包含两个词的结果。如果一个 Book
的 title
中包含 war
,而 summary
中包含 peace
,应被视为有效结果。
这对 Hibernate Search 有什么影响?
到目前为止,为了使用 Hibernate Search 满足这些要求,你有以下几种可能性
- 使用 DSL
-
-
手动将搜索查询分割成关键词
-
手动构建一个(相当复杂的)查询,使用 DSL 的
keyword()
入口(keyword()
只支持OR
,所以你需要为每个关键词和每个字段构建几个不同的keyword()
查询) -
缺点
-
不可能允许用户选择性地输入短语查询、模糊查询等,而不需要实现解析器(或者有复选框选项来启用它们)
-
如果你想在超过 2 个字段中进行搜索,可能会产生大量的样板代码
-
-
- 使用 Lucene 的
MultiFieldQueryParser
-
-
相当高效
-
缺点
-
不宽容:可能会抛出
ParseException
-
你暴露了 Lucene 默认
QueryParser
的全部功能,这可能不是你想要的 -
你可以定义默认字段,但用户可以使用
field:keyword
语法搜索其他字段:这可能不是你想要的
-
-
我们如何使用新的 simpleQueryString()
DSL 入口来做这件事?
就像这样简单
String simpleQueryString = "war peace"; // what the end user is looking for
QueryBuilder qb = fullTextSession.getSearchFactory()
.buildQueryBuilder()
.forEntity( Book.class)
.get(); // instantiate the QueryBuilder providing the DSL
Query query = qb.simpleQueryString()
.onField( "title" ).boostedTo( 5.0f )
.andField( "summary" ).boostedTo( 2.0f )
.withAndAsDefaultOperator() // we want AND to be the default operator
.matching( simpleQueryString )
.createQuery();
List<Book> results = fullTextSession.createFullTextQuery( query, Book.class ).getResultList();
重要精度:如果你没有显式定义操作符,默认操作符是 OR
。在一般情况下,这可能不是你想要的,因此示例中调用了 withAndAsDefaultOperator()
。
如果你想也在 author
字段中进行搜索,只需添加另一个 andField()
子句即可。
我最喜欢的是,你可以与 Lucene 索引定义一个简单的契约,并且用户可以在你定义的契约内部定义更高级的查询,有一定的灵活性
-
-war peace
:包含peace
但不包含war
的文档 -
war | peace
:包含war
或peace
的文档(如果你将其定义为默认值,可以省略|
操作符) -
war + pea*
:包含war
和至少一个以pea
开头的单词的文档(如果你将其定义为默认值,可以省略+
操作符) -
"war and peace"
:包含短语war and peace
的文档 -
pease~
:包含pease
的文档,进行模糊搜索,因此包含peace
的文档也将被返回 -
war + (peace | harmony)
:包含war
和peace
或harmony
的文档 -
以及上述任何组合……
-
或者没有:简单的搜索显然也是支持的!
你怎么看待这个?
还在吗?我们有一些问题要问你。
首先,经过大量讨论,我们决定将DSL入口点命名为simpleQueryString()
。它具有与Elasticsearch保持一致的优点。如果您能想到更好的(也许更明确)的名称,现在加入讨论还不算太晚!一旦我们确定,我们将坚持使用这个名字永远。
欢迎您在下面的评论或hibernate-dev邮件列表上提供反馈。
其次,目前我们还没有提供禁用特定功能的能力。默认情况下,以下功能都是启用的:
-
布尔运算符(
+
、-
和|
) -
优先级运算符(括号)
-
短语搜索(
"some phrase"
) -
前缀运算符(
prefix*
) -
模糊运算符(
fuzzy~
) -
短语搜索的slop(
"some phrase"~2
)
认为能够禁用功能可能会有用吗?请随时在我们的JIRA上创建一个问题,或者更好的是提出一个拉取请求!您可以在https://github.com/hibernate/hibernate-search/pull/1318找到原始补丁,它可能有助于您开始。