分享

介绍

在显示或检索时对数据进行排序是大多数数据库系统的一项关键操作,它有助于将它们与其他数据存储机制区分开来。能够独立于其存储顺序来操纵各个字段的排序、优先级和解释是数据库本身及其相关查询系统最实用的功能之一。

MongoDB 提供了许多方法来控制从查询返回的数据的排序方式。在本指南中,我们将介绍如何根据您的用例以各种方式对数据进行排序。我们将介绍简单的和复合的排序,如何更改排序顺序,以及如何将排序与其他运算符结合使用。

设置示例数据

为了演示排序的工作原理,我们将查询 students 集合 中包含的多个文档。您可以通过复制粘贴以下内容来创建 students 集合并插入我们将要查询的文档

db.students.insertMany([
{
first_name: 'Carol',
last_name: 'Apple',
dob: ISODate('2010-10-30'),
address: {
street: {
name: 'Flint Rd.',
number: '803',
},
city: 'Camden',
zip: '10832',
},
},
{
first_name: 'Spencer',
last_name: 'Burton',
dob: ISODate('2008-12-04'),
address: {
street: {
name: 'Edgecombe St.',
number: '2083b',
},
city: 'Zoofreid',
zip: '80828',
},
},
{
first_name: 'Nixie',
last_name: 'Languin',
dob: ISODate('2011-02-11'),
address: {
street: {
name: 'Kensington Ln.',
number: '33',
},
city: 'Zoofreid',
zip: '80829',
},
},
{
first_name: 'Anthony',
last_name: 'Apple',
dob: ISODate('2009-08-16'),
address: {
street: {
name: 'Flint Rd.',
number: '803',
},
city: 'Camden',
zip: '10832',
},
},
{
first_name: 'Rose',
last_name: 'Southby',
dob: ISODate('2011-03-03'),
address: {
street: {
name: 'Plainfield Dr.',
number: '4c',
},
city: 'Nambles',
zip: '38008',
},
},
{
first_name: 'Lain',
last_name: 'Singh',
dob: ISODate('2013-06-22'),
address: {
street: {
name: 'Plainfield Dr.',
number: '308',
},
city: 'Brighton',
zip: '18002',
},
},
])

插入上述文档后,继续下一节以了解简单的排序。

如何排序单个字段

在 MongoDB 中排序结果的基本方法是在查询上附加 .sort() 方法。 .sort() 方法接受一个文档作为参数,指定要排序的字段以及排序方向。

排序结果最基本的方法是提供一个文档,该文档指定一个单个字段,该字段指示列名,值为 1 表示升序排序

请注意,我们在 .find() 的第二个参数中提供了一个 MongoDB 投影,以便仅显示某些字段。我们还附加了 .pretty() 方法以使输出更易读。

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
dob: 1,
}
)
.sort({
dob: 1,
})
.pretty()

上面的查询将按学生的出生日期返回,默认升序排列

{
"first_name" : "Spencer",
"last_name" : "Burton",
"dob" : ISODate("2008-12-04T00:00:00Z")
}
{
"first_name" : "Anthony",
"last_name" : "Apple",
"dob" : ISODate("2009-08-16T00:00:00Z")
}
{
"first_name" : "Carol",
"last_name" : "Apple",
"dob" : ISODate("2010-10-30T00:00:00Z")
}
{
"first_name" : "Nixie",
"last_name" : "Languin",
"dob" : ISODate("2011-02-11T00:00:00Z")
}
{
"first_name" : "Rose",
"last_name" : "Southby",
"dob" : ISODate("2011-03-03T00:00:00Z")
}
{
"first_name" : "Lain",
"last_name" : "Singh",
"dob" : ISODate("2013-06-22T00:00:00Z")
}

要反转排序顺序,请将排序列设置为 -1 而不是 1

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
dob: 1,
}
)
.sort({
dob: -1,
})
.pretty()
{
"first_name" : "Lain",
"last_name" : "Singh",
"dob" : ISODate("2013-06-22T00:00:00Z")
}
{
"first_name" : "Rose",
"last_name" : "Southby",
"dob" : ISODate("2011-03-03T00:00:00Z")
}
{
"first_name" : "Nixie",
"last_name" : "Languin",
"dob" : ISODate("2011-02-11T00:00:00Z")
}
{
"first_name" : "Carol",
"last_name" : "Apple",
"dob" : ISODate("2010-10-30T00:00:00Z")
}
{
"first_name" : "Anthony",
"last_name" : "Apple",
"dob" : ISODate("2009-08-16T00:00:00Z")
}
{
"first_name" : "Spencer",
"last_name" : "Burton",
"dob" : ISODate("2008-12-04T00:00:00Z")
}

如何按附加字段排序

对于主排序字段包含重复值的情况,MongoDB 可以使用其他字段来控制排序。为此,您可以在传递给 sort() 函数的文档中传递其他字段及其排序顺序。

例如,如果我们按 last_namestudent 文档进行排序,我们可以获得按该字段排序的学生字母列表

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
}
)
.sort({
last_name: 1,
})
.pretty()
{ "first_name" : "Carol", "last_name" : "Apple" }
{ "first_name" : "Anthony", "last_name" : "Apple" }
{ "first_name" : "Spencer", "last_name" : "Burton" }
{ "first_name" : "Nixie", "last_name" : "Languin" }
{ "first_name" : "Lain", "last_name" : "Singh" }
{ "first_name" : "Rose", "last_name" : "Southby" }

但是,有两个学生的姓氏都是 "Apple",并且返回的排序在考虑其名字时不是字母顺序。

为了解决这个问题,我们可以使用 first_name 作为次要排序字段

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
}
)
.sort({
last_name: 1,
first_name: 1,
})
.pretty()
{ "first_name" : "Anthony", "last_name" : "Apple" }
{ "first_name" : "Carol", "last_name" : "Apple" }
{ "first_name" : "Spencer", "last_name" : "Burton" }
{ "first_name" : "Nixie", "last_name" : "Languin" }
{ "first_name" : "Lain", "last_name" : "Singh" }
{ "first_name" : "Rose", "last_name" : "Southby" }

经过进一步的规范,结果与我们对姓名所期望的传统字母顺序相匹配。

如何使用嵌入式文档字段排序

MongoDB 还可以根据嵌入式文档中包含的值对结果进行排序。为此,请使用 点表示法 钻取到嵌入式文档中的相应字段。

例如,您可以根据学生居住的citystudent数据进行排序,该city是每个文档中address的组成部分。请记住,在使用点表示法时,需要引用字段名称以确保它们被正确解释。

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
'address.city': 1,
}
)
.sort({
'address.city': 1,
})
.pretty()
{
"first_name" : "Lain",
"last_name" : "Singh",
"address" : {
"city" : "Brighton"
}
}
{
"first_name" : "Carol",
"last_name" : "Apple",
"address" : {
"city" : "Camden"
}
}
{
"first_name" : "Anthony",
"last_name" : "Apple",
"address" : {
"city" : "Camden"
}
}
{
"first_name" : "Rose",
"last_name" : "Southby",
"address" : {
"city" : "Nambles"
}
}
{
"first_name" : "Spencer",
"last_name" : "Burton",
"address" : {
"city" : "Zoofreid"
}
}
{
"first_name" : "Nixie",
"last_name" : "Languin",
"address" : {
"city" : "Zoofreid"
}
}

您可以将此与其他排序字段结合使用,以确保结果按您期望的顺序排序。

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
'address.city': 1,
'address.street': 1,
}
)
.sort({
'address.city': 1,
'address.street.name': 1,
'address.street.number': 1,
last_name: 1,
first_name: 1,
})
.pretty()

在本例中,我们按以下字段排序:

  • 城市
  • 街道名称
  • 街道号码
  • 姓氏
  • 名字

查询结果如下所示:

{
"first_name" : "Lain",
"last_name" : "Singh",
"address" : {
"street" : {
"name" : "Plainfield Dr.",
"number" : "308"
},
"city" : "Brighton"
}
}
{
"first_name" : "Anthony",
"last_name" : "Apple",
"address" : {
"street" : {
"name" : "Flint Rd.",
"number" : "803"
},
"city" : "Camden"
}
}
{
"first_name" : "Carol",
"last_name" : "Apple",
"address" : {
"street" : {
"name" : "Flint Rd.",
"number" : "803"
},
"city" : "Camden"
}
}
{
"first_name" : "Rose",
"last_name" : "Southby",
"address" : {
"street" : {
"name" : "Plainfield Dr.",
"number" : "4c"
},
"city" : "Nambles"
}
}
{
"first_name" : "Spencer",
"last_name" : "Burton",
"address" : {
"street" : {
"name" : "Edgecombe St.",
"number" : "2083b"
},
"city" : "Zoofreid"
}
}
{
"first_name" : "Nixie",
"last_name" : "Languin",
"address" : {
"street" : {
"name" : "Kensington Ln.",
"number" : "33"
},
"city" : "Zoofreid"
}
}

现在也是时候提一下,用于排序的字段**不必**是您用于投影的字段的子集。

例如,我们可以实现完全相同的排序,但只返回学生姓名,方法是:

db.students
.find(
{},
{
_id: 0,
first_name: 1,
last_name: 1,
}
)
.sort({
'address.city': 1,
'address.street.name': 1,
'address.street.number': 1,
last_name: 1,
first_name: 1,
})
.pretty()

查询返回以下数据:

{ "first_name" : "Lain", "last_name" : "Singh" }
{ "first_name" : "Anthony", "last_name" : "Apple" }
{ "first_name" : "Carol", "last_name" : "Apple" }
{ "first_name" : "Rose", "last_name" : "Southby" }
{ "first_name" : "Spencer", "last_name" : "Burton" }
{ "first_name" : "Nixie", "last_name" : "Languin" }

如果您将结果与上一个查询的结果进行比较,您就可以验证文档是否已按相同顺序返回。

结论

在这篇文章中,我们了解了如何使用sort()方法来控制 MongoDB 如何对查询结果进行排序。我们涵盖了单字段排序、按优先级排序多个字段、更改排序顺序以及根据嵌入式文档字段进行排序。

文档校对结果限制等功能相结合,排序使您能够精确控制文档和字段之间的比较方式以及它们的返回方式。熟悉这些功能可以帮助您编写更好的查询,并将数据返回到更接近您使用方式的状态。

关于作者
Justin Ellingwood

Justin Ellingwood

Justin 自 2013 年以来一直在撰写有关数据库、Linux、基础设施和开发人员工具的文章。他目前与妻子和两只兔子住在柏林。他通常不必以第三人称写作,这对所有相关人员来说都是一种解脱。