Hibernate Search 是一个库,它通过自动索引实体将 Hibernate ORM 与 Apache Lucene 或 Elasticsearch 集成,从而实现高级搜索功能:全文搜索、地理空间搜索、聚合等。更多信息,请参阅 hibernate.org 上的 Hibernate Search。

最近的 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 的权重,并且只返回包含两个词的结果。如果一个 Booktitle 中包含 war,而 summary 中包含 peace,应被视为有效结果。

到目前为止,为了使用 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:包含 warpeace 的文档(如果你将其定义为默认值,可以省略 | 操作符)

  • war + pea*:包含 war 和至少一个以 pea 开头的单词的文档(如果你将其定义为默认值,可以省略 + 操作符)

  • "war and peace":包含短语 war and peace 的文档

  • pease~:包含 pease 的文档,进行模糊搜索,因此包含 peace 的文档也将被返回

  • war + (peace | harmony):包含 warpeaceharmony 的文档

  • 以及上述任何组合……

  • 或者没有:简单的搜索显然也是支持的!

你怎么看待这个?

还在吗?我们有一些问题要问你。

首先,经过大量讨论,我们决定将DSL入口点命名为simpleQueryString()。它具有与Elasticsearch保持一致的优点。如果您能想到更好的(也许更明确)的名称,现在加入讨论还不算太晚!一旦我们确定,我们将坚持使用这个名字永远

欢迎您在下面的评论或hibernate-dev邮件列表上提供反馈。

其次,目前我们还没有提供禁用特定功能的能力。默认情况下,以下功能都是启用的:

  • 布尔运算符(+-|

  • 优先级运算符(括号)

  • 短语搜索("some phrase"

  • 前缀运算符(prefix*

  • 模糊运算符(fuzzy~

  • 短语搜索的slop("some phrase"~2

认为能够禁用功能可能会有用吗?请随时在我们的JIRA上创建一个问题,或者更好的是提出一个拉取请求!您可以在https://github.com/hibernate/hibernate-search/pull/1318找到原始补丁,它可能有助于您开始。


回到顶部