对于 AI 代理:可在 https://www.mongodb.com/zh-cn/docs/llms.txt 获取文档索引—通过在任何 URL 路径后添加 .md 可获取所有页面的 Markdown 版本。
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Docs 菜单

修改查询结果

在本指南中,您可以学习;了解如何使用MongoDB Extension for Hibernate ORM 对查询结果进行排序、跳过、限制和群组。

您可以在查询声明中使用 order by 子句,按指定字段的值对查询结果进行排序。默认下,order by 子句按升序对文档进行排序。要按降序对文档进行排序,请将 desc 关键字附加到字段名称。

The following example retrieves matching documents from the sample_mflix.movies collection and sorts them by the year field in descending order:

var sortResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class)
.setParameter("t", "Cinderella")
.getResultList();
for (var m : sortResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}
var sortResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class)
.setParameter("t", "Cinderella")
.getResultList();
for (var m : sortResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}

您可以通过将 setFirstResult() 方法链接到查询来跳过查询结果中指定数量的文档。将要跳过的初始文档数作为参数传递给此方法。

The following example runs the same query as the preceding example but skips the first matching document in the results:

var skipResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class)
.setParameter("t", "Cinderella")
.setFirstResult(1)
.getResultList();
for (var m : skipResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}
var skipResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class)
.setParameter("t", "Cinderella")
.setFirstResult(1)
.getResultList();
for (var m : skipResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}

您可以通过将 setMaxResults() 方法链接到查询来限制查询返回的文档数量。将要返回的最大文档数作为参数传递给此方法。

The following example runs the same query as the sort example but returns up to two matching documents in the results:

var limitResult = session.createQuery("from Movie where title = :t order by year desc", Movie.class)
.setParameter("t", "Cinderella")
.setMaxResults(2)
.getResultList();
for (var m : limitResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}
var limitResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc", Movie.class)
.setParameter("t", "Cinderella")
.setMaxResults(2)
.getResultList();
for (var m : limitResult) {
System.out.println("Title: " + m.getTitle() + ", Year: " + m.getYear());
}

或者,可以在 HQL 或 JPQL声明中使用 limit 子句来限制查询返回的文档数量,如以下代码所示:

var limitClauseResult = session.createQuery("from Movie where title = :t order by year desc limit 2", Movie.class)
.setParameter("t", "Cinderella")
.getResultList();
var limitClauseResult = entityManager.createQuery("select m from Movie m where m.title = :t order by m.year desc limit 2", Movie.class)
.setParameter("t", "Cinderella")
.getResultList();

您可以通过在查询声明中使用 group by 子句将股票值的文档折叠到单个结果行中。 Hibernate ORM 扩展将 group by 子句转换为MongoDB $群组阶段,其中每个分组键成为 _id字段的子字段。

重要

对关键要求进行分组

在分组查询的 select、having 或 order by 子句中引用的每个字段都必须是分组键或其一部分。如果引用 $group 阶段未生成的字段,则 Hibernate ORM 扩展会抛出 FeatureNotSupportedException。

分组查询也不能使用 distinct 关键字。

要按一个或多个字段的值对结果进行群组,请在 group by 子句中列出这些字段。要按多个字段群组,请用逗号分隔字段名称。

此示例执行以下操作:

  • 从 sample_mflix.movies集合中检索 title 值为 "Hairspray" 的文档。

  • 按 year字段对电影进行分组,该字段为每个不同的发布年份返回一行。

  • 按 year 值升序对结果进行排序。

var groupResult = session.createQuery(
"select year from Movie where title = :title group by year order by year",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var year : groupResult) {
System.out.println("Year: " + year);
}
var groupResult = entityManager.createQuery(
"select m.year from Movie m where m.title = :title group by m.year order by m.year",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var year : groupResult) {
System.out.println("Year: " + year);
}

Hibernate ORM 扩展将此查询转换为以下管道。 where 子句成为在分组之前运行的 $match 阶段。 order by 子句成为根据分组键进行排序的 $sort 阶段:

[
{ "$match": { "title": { "$eq": "Hairspray" } } },
{ "$group": { "_id": { "year": "$year" } } },
{ "$sort": { "_id.year": 1 } },
{ "$project": { "_id#year": "$_id.year", "_id": 0 } }
]

分组键可以是计算表达式,而不是字段引用。 Hibernate ORM 扩展将表达式存储在生成的名为 k<n> 的 _id 子字段中。当您在 select 或 having 子句中写入相同的表达式时,Hibernate ORM 扩展会将其解析为该子字段,而不是重新计算。

仅当您以相同方式写入表达式和分组键时,Hibernate ORM 扩展才会将二者匹配。示例,year + 1 的键与 1 + year 的出现不匹配。

您还可以按计算表达式键进行排序。在 order by 子句中编写表达式与在 group by 子句中写入的表达式完全相同。

以下示例将 title 为 "Hairspray" 的文档按发布年代进行分组,并按升序对结果进行排序:

var groupExpressionResult = session.createQuery(
"select year / 10 * 10 from Movie where title = :title group by year / 10 * 10 order by year / 10 * 10",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var decade : groupExpressionResult) {
System.out.println("Decade: " + decade);
}
var groupExpressionResult = entityManager.createQuery(
"select m.year / 10 * 10 from Movie m where m.title = :title group by m.year / 10 * 10 order by m.year / 10 * 10",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var decade : groupExpressionResult) {
System.out.println("Decade: " + decade);
}

Hibernate ORM 扩展将此查询转换为以下管道,其中 $sort 和 $project 阶段从 _id.k0 读取分组键,而不是重新计算表达式:

[
{ "$match": { "title": { "$eq": "Hairspray" } } },
{
"$group": {
"_id": {
"k0": {
"$multiply": [
{ "$toInt": { "$divide": [ "$year", 10 ] } },
10
]
}
}
}
},
{ "$sort": { "_id.k0": 1 } },
{ "$project": { "#c_1": "$_id.k0", "_id": 0 } }
]

您还可以选择包含分组键的计算表达式。在这种情况下,Hibernate ORM 扩展按键进行分组,并在 $project 阶段根据键计算表达式。

要学习;了解有关计算表达式的更多信息,请参阅《指定查询》指南中的使用计算表达式部分。

要从结果中删除群组,请使用 having 子句。 Hibernate ORM 扩展使用 having 子句来根据分组键进行过滤。您可以使用 and 和 or 操作符组合多个条件。

此示例执行以下操作:

  • 从 sample_mflix.movies集合中检索 "Hairspray" 电影。

  • 按 year字段对电影进行分组,该字段为每个不同的发布年份返回一行。

  • 筛选群组,仅包含大于 2000 的 year 值。

  • 按年份升序对结果进行排序。

var havingResult = session.createQuery(
"select year from Movie where title = :title group by year having year > 2000 order by year",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var year : havingResult) {
System.out.println("Year: " + year);
}
var havingResult = entityManager.createQuery(
"select m.year from Movie m where m.title = :title group by m.year having m.year > 2000 order by m.year",
Integer.class)
.setParameter("title", "Hairspray")
.getResultList();
for (var year : havingResult) {
System.out.println("Year: " + year);
}

Hibernate ORM 扩展将此查询转换为以下管道。 where 子句成为在分组之前运行的 $match 阶段。 having 子句成为在分组之后运行的第二个 $match 阶段,并根据分组键进行筛选:

[
{ "$match": { "title": { "$eq": "Hairspray" } } },
{ "$group": { "_id": { "year": "$year" } } },
{ "$match": { "_id.year": { "$gt": 2000 } } },
{ "$sort": { "_id.year": 1 } },
{ "$project": { "_id#year": "$_id.year", "_id": 0 } }
]

要了解有关对MongoDB数据执行其他操作的更多信息,请参阅CRUD 操作指南。

要了解有关使用 HQL 和 JPQL 运行查询的更多信息,请参阅 Hibernate ORM 文档中的 Hibernate 查询语言指南。