休眠
Hibernate 本身不支持全文搜索。它必须依赖数据库引擎支持或第三方解决方案。
名为 Hibernate Search 的扩展与 Apache Lucene 或 Elasticsearch(还与 OpenSearch 集成)。
Postgres
Postgres 具有全文搜索</a > 7.3 版以来的功能。虽然它无法与 Elasticsearch 或 Lucene 等搜索引擎竞争,但它仍然提供了灵活且强大的解决方案,可能足以满足应用程序用户的期望——词干提取、排名和索引等功能。
我们将简要解释如何在 Postgres 中进行全文搜索。有关更多信息,请访问 Postgres 文档。对于基本的文本匹配,最关键的部分是数学运算符@@
。
如果文档(tsvector
类型的对象)与查询(tsquery
类型的对象)匹配,则返回 true
。
顺序对于操作员来说并不重要。因此,如果我们将文档放在运算符的左侧并将查询放在右侧或以不同的顺序,这并不重要。
为了更好地演示,我们使用一个名为 tweet
的数据库表。
创建表 tweet (
id bigint 不为空,
短内容 varchar(255),
标题 varchar(255),
主键(id)
)
有了这样的数据:
INSERT INTO tweet (id, title, Short_content) VALUES (1, '猫', '猫统治世界');
INSERT INTO tweet (id, title, Short_content) VALUES (2, '老鼠', '下水道里的老鼠规则');
INSERT INTO tweet (id, title, Short_content) VALUES (3, '老鼠与猫', '老鼠和猫互相讨厌');
INSERT INTO tweet (id, title, Short_content) VALUES (4, 'Feature', '该项目旨在包装 Postgres 已存在的功能');
INSERT INTO tweet (id, title, Short_content) VALUES (5, 'Postgres 数据库', 'Postgres 是市场上广泛使用的数据库之一');
INSERT INTO tweet (id, title, Short_content) VALUES (6, '数据库', '市场上有很多数据库具有类似 Oracle 的功能');
现在让我们看看每条记录的 short_content
列的 tsvector
对象是什么样子的png” data-new=”false” data-size=”19408″ data-sizeformatted=”19.4 kB” data-src=”https://dz2cdn1.dzone.com/storage/temp/17430962-1704633123741.png” 数据-type=”temp” data-url=”https://dz2cdn1.dzone.com/storage/temp/17430962-1704633123741.png” src=”http://www.cheeli.com.cn/wp-content/ uploads/2024/01/17430962-1704633123741.png” style=”width: 765px;”/>
输出显示了 to_tsvcector
如何将文本列转换为“english
”文本搜索配置的 tsvector
对象。
文本搜索配置
上例中传递的 to_tsvector 函数的第一个参数是文本搜索配置的名称。在这种情况下,它是“english
”。根据Postgres文档,文本搜索配置如下:
<块引用>
…全文搜索功能包括执行更多操作的能力:跳过索引某些单词(停用词)、处理同义词以及使用复杂的解析,例如,不仅仅基于空格进行解析。此功能由文本搜索配置控制。
</块引用>
因此,配置是该过程的关键部分,对于我们的全文搜索结果至关重要。对于不同的配置,Postgres引擎可以返回不同的结果。不同语言的词典之间不一定是这种情况。例如,您可以对同一种语言有两种配置,但一种配置会忽略包含数字的名称(例如,某些序列号)。如果我们在查询中传递我们正在寻找的特定序列号(这是强制性的),我们将找不到任何忽略带有数字的单词的配置记录。即使我们的数据库中有这样的记录,请检查 配置文档了解更多信息。
文本查询
文本查询支持&
(AND)、|
(OR)、!
(NOT)和<等运算符->
(后面是)。前三个运算符不需要更深入的解释。 <->
运算符检查单词是否存在以及它们是否按特定顺序放置。因此,例如,对于查询“rat <-> cat
”,我们期望“cat”单词将存在,后面跟着“rat”。
示例
- 包含老鼠和猫的内容:
选择 tShort_content FROM tweet t WHERE to_tsvector('english', t.short_content) @@ to_tsquery('english', 'Rat & cat');
- 包含数据库和市场的内容,并且市场是数据库之后的第三个词:
市场’);”数据-lang =“文本/x-sql”>
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@ to_tsquery('english', '数据库 <3>市场');
- 包含数据库但不包含Postgres的内容:
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@ to_tsquery('english', 'database & !Postgres ');
- 包含 Postgres 或 Oracle 的内容:
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@ to_tsquery('english', 'Postgres | Oracle' );
包装函数
本文中已经提到了创建文本查询的包装函数之一,即 to_tsquery
。还有更多这样的功能,例如:
plainto_tsquery
phraseto_tsquery
websearch_to_tsquery
plainto_tsquery
plainto_tsquery
将所有传递的单词转换为查询,其中所有单词均使用 &
(AND) 运算符组合。例如,plainto_tsquery('english', 'Rat cat')
的等效项是 to_tsquery('english', 'Rat & cat')
。
对于以下用途:
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@ plainto_tsquery('english', 'Rat cat') ;
我们得到以下结果:
phraseto_tsquery
phraseto_tsquery
将所有传递的单词转换为查询,其中所有单词均与 <->
(FOLLOW BY) 运算符组合。例如,phraseto_tsquery('english', 'cat Rule')
的等效项是 to_tsquery('english', 'cat <->rule')
。 p>
对于以下用途:
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@phraseto_tsquery('english', 'cat Rule') ;
我们得到以下结果:
websearch_to_tsquery
websearch_to_tsquery 使用替代语法创建有效的文本查询。
- 不带引号的文本:以与
plainto_tsquery
相同的方式转换部分语法 - 引用文本:以与
phraseto_tsquery
相同的方式转换部分语法 - OR:转换为“
|
”(OR) 运算符 - “
-
”:与“!
”(非)运算符相同
例如,websearch_to_tsquery('english', '"cat Rule" or database -Postgres')
的等效项是 to_tsquery('english', 'cat <->rule | 数据库 & !Postgres')
.
对于以下用途:
从推文 t 中选择 t.id、t.short_content,其中 to_tsvector('english', t.short_content) @@ websearch_to_tsquery('english', '"cat Rule"或数据库-Postgres');
我们得到以下结果:
Postgres 和 Hibernate 本机支持
正如文章中提到的,仅 Hibernate 不支持全文搜索。它必须依赖数据库引擎的支持。这意味着我们可以执行本机 SQL 查询,如下例所示:
plainto_tsquery
返回entityManager.createNativeQuery(String.format(“select * from tweet t1_0 where to_tsvector(‘%1$s’, t1_0.short_content) @@ plainto_tsquery(‘%1$s’, :textQuery)”, configuration), Tweet。 class).setParameter(“textQuery”, textQuery).getResultList();
}” data-lang=”text/x-java”>
公共列表 findBySinglePlainQueryInDescriptionForConfigurationWithNativeSQL(字符串textQuery,字符串配置){
返回entityManager.createNativeQuery(String.format("select * from tweet t1_0 where to_tsvector('%1$s', t1_0.short_content) @@ plainto_tsquery('%1$s', :textQuery)", configuration), Tweet。 class).setParameter("textQuery", textQuery).getResultList();
}
websearch_to_tsquery
返回实体管理器format(“select * from tweet t1_0 where to_tsvector(‘%1$s’, t1_0.short_content) @@ websearch_to_tsquery(‘%1$s’, :textQuery)”, configuration), Tweet.class).setParameter(“textQuery “, textQuery).getResultList();
}” data-lang=”text/x-java”>
公共列表 findCorrectTweetsByWebSearchToTSQueryInDescriptionWithNativeSQL(字符串textQuery,字符串配置){
返回entityManager.createNativeQuery(String.format("select * from tweet t1_0 where to_tsvector('%1$s', t1_0.short_content) @@ websearch_to_tsquery('%1$s', :textQuery)", configuration), Tweet。 class).setParameter("textQuery", textQuery).getResultList();
}
Hibernate 与 posjsonhelper 库
posjsonhelper 库是一个开放的- 源项目添加了对 PostgreSQL JSON 函数 和全文搜索的 Hibernate 查询支持.
<依赖项>
com.github.starnowski.posjsonhelper.text
hibernate6-text
<版本>0.3.0</版本>
</依赖>
<依赖关系>
org.hibernate
hibernate-core
<版本>6.4.0.最终版</版本>
</依赖> </代码> </预>
要使用 posjsonhelper
库中存在的组件,我们需要在 Hibernate 上下文中注册它们。
这意味着必须有一个指定的 org.hibernate.boot.model.FunctionContributor
实现。该库具有此接口的实现,即 com.github.starnowski.posjsonhelper.hibernate6.PosjsonhelperFunctionContributor要使用此实现,需要“resources/META-INF/services”目录下的“hibernate.boot.model.FunctionContributor”。
还有另一种方法可以注册 posjsonhelper 的组件,可以通过可编程性来完成。要了解如何执行此操作,请检查此 链接。
现在,我们可以在 Hibernate 查询中使用全文搜索运算符。
PlainToTSQueryFunction
这是一个包装 plainto_tsquery 函数的组件。</ p>
公共列表 findBySinglePlainQueryInDescriptionForConfiguration(字符串textQuery,字符串配置){
CriteriaBuilder cb =EntityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Tweet.class);
Root root = query.from(Tweet.class);
查询。选择(根);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), 配置, (NodeBuilder) cb), new PlainToTSQueryFunction((NodeBuilder) cb, 配置, textQuery), hibernateContext));
返回entityManager.createQuery(query).getResultList();
}
对于值为 'english'
的配置,代码将生成以下语句:
选择
t1_0短内容,
t1_0.标题
从
推特 t1_0
在哪里
to_tsvector('english', t1_0.short_content) @@ plainto_tsquery('english', ?);
PhraseToTSQueryFunction
此组件包装了 phraseto_tsquery 函数。
公共列表 findBySinglePhraseInDescriptionForConfiguration(字符串textQuery,字符串配置){
CriteriaBuilder cb =EntityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Tweet.class);
Root root = query.from(Tweet.class);
查询。选择(根);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), 配置, (NodeBuilder) cb), new PhraseToTSQueryFunction((NodeBuilder) cb, 配置, textQuery), hibernateContext));
返回entityManager.createQuery(query).getResultList();
}
对于值为 'english'
的配置,代码将生成以下语句:
选择
t1_0.id,
t1_0.short_content,
t1_0.标题
从
推特 t1_0
在哪里
to_tsvector('english', t1_0.short_content) @@phraseto_tsquery('english', ?)
WebsearchToTSQueryFunction
此组件包装 websearch_to_tsquery 函数。
公共列表 findCorrectTweetsByWebSearchToTSQueryInDescription(字符串短语, 字符串配置) {
CriteriaBuilder cb =EntityManager.getCriteriaBuilder();
CriteriaQuery query = cb.createQuery(Tweet.class);
Root root = query.from(Tweet.class);
查询。选择(根);
query.where(new TextOperatorFunction((NodeBuilder) cb, new TSVectorFunction(root.get("shortContent"), 配置, (NodeBuilder) cb), new WebsearchToTSQueryFunction((NodeBuilder) cb, 配置, 短语), hibernateContext));
返回entityManager.createQuery(query).getResultList();
}
对于值为 'english'
的配置,代码将生成以下语句:
选择
t1_0.id,
t1_0.short_content,
t1_0.标题
从
推特 t1_0
在哪里
to_tsvector('english', t1_0.short_content) @@ websearch_to_tsquery('english', ?)
HQL 查询
所有提到的组件都可以在 HQL 查询中使用。要检查如何完成,请单击此 链接。
当我们可以使用 Hibernate 的本机方法时,为什么要使用 posjsonhelper 库?
虽然动态连接应该是 HQL 或 SQL 查询的字符串可能很容易,但实现谓词会是更好的做法,特别是当您必须根据 API 的动态属性处理搜索条件时这可以使我们免于做出向我们的技术堆栈添加第三方解决方案的决定,这也可能会增加更多的复杂性和额外的成本。