Overview
在本指南中,您可以学习;了解如何使用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 查询语言指南。