分享到

简介

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

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 表示升序排序

请注意,我们提供了一个 MongoDB 投影 作为 .find() 的第二个参数,以仅显示某些字段。我们还附加了 .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 还可以根据嵌入式文档中包含的值对结果进行排序。为此,请使用 点表示法 深入到嵌入式文档中的相应字段。

例如,您可以根据 student 数据居住的 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、基础设施和开发者工具的文章。他目前与妻子和两只兔子住在柏林。他通常不必以第三人称写作,这对所有相关方来说都是一种解脱。