十:文档 Document 查询高级篇

一灰灰blogDBMongoMongoDb约 1274 字大约 4 分钟

上一篇的mongodb查询,主要介绍的是一些基本操作,当然有基本就高阶操作;

本文将带来更多的查询姿势

  • 排序
  • 分页
  • 聚合

1. 排序

在mongodb中,使用sort方法进行排序,语法如下

db.collection.find().sort({key: 1})

请注意,sort内部是一个对象,key为field,value为1或者-1,其中1表示升序,-1表示降序

实例说明,根据age进行排序

db.doc_demo.find().sort({'age': 1})

输出如下:

上面的演示属于常规的操作,但是针对mongodb的特点,自然会有一些疑问

q1: 如果某个文档没有包含这个field,排序是怎样的?

db.doc_demo.find().sort({'tag': 1})

从输出来看,升序时,不包含这个field的文档,在最前面;降序时,不包含这个field的文档,在最后面

q2: 支持多个field排序吗?

原则上一般不建议多个field的排序(比较影响性能),但对于数据库而言,你得支持吧

# 在开始之前,先改一下tag,让文档不完全一致
db.doc_demo.update({"_id": ObjectId("5e7b5ac10172dc950171c488")}, {$set: {'tag': 2}})
db.doc_demo.update({"_id": ObjectId("5e7b5bb085a742842d2e23fc")}, {$set: {'tag': 2}})

# 先根据age进行升序排,当age相同的,根据tag降序排
db.doc_demo.find().sort({'age': 1, 'tag': -1})
# 先根据tag进行升序排,tag相同的,根据age升序排
db.doc_demo.find().sort({'tag': 1, 'age': 1})

请注意上的输出,在涉及到多个field排序时,优先根据第一个进行排序,当文档的field相同时,再根据后面的进行排序

2. 分页

当文档很多时,我们不可能把所有的文档一次返回,所以就有了常见的分页,在sql中我们一般使用limit offset来实现分页,在mongodb中也差不多

limit()

限制返回的文档数

db.doc_demo.find().limit(2)

skip()

使用limit进行返回条数限制,使用skip进行分页,表示跳过前面的n条数据

# 跳过第一条数据,返回两条; 相当于返回第2、3条数据
db.doc_demo.find().limit(2).skip(1)

3. 聚合

使用aggregate()来实现聚合,用于处理求和、平均值,最大值,分组等

数据准备:

{ "_id" : ObjectId("5e7b5ac10172dc950171c488"), "name" : "一灰灰blog", "age" : "19", "skill" : [ "java", "python", "sql" ], "tag" : 2 }
{ "_id" : ObjectId("5e7b5ac40172dc950171c489"), "name" : "一灰灰blog", "age" : 20, "skill" : [ "web", "shell", "js" ], "tag" : 1 }
{ "_id" : ObjectId("5e7b5bb085a742842d2e23fc"), "name" : "一灰灰", "age" : 18, "sex" : "man", "tag" : 2 }
{ "_id" : ObjectId("5e7b5c2e0172dc950171c48a"), "name" : "一灰灰", "age" : 18, "hobby" : [ "play game" ] }

分组查询

根据name进行分组统计

# 根据name进行分组,统计文档数量
# 相当于sql中的  select name, count(1) from doc_demo group by name
db.doc_demo.aggregate([{$group: {_id: "$name", size: {$sum: 1}}}])

请注意,分组的条件中

  • _id: 表示根据哪个字段进行分组
  • size: {}: 表示聚合条件指定,将结果输出到名为size的field中
  • filed名前加$进行指定

当前mongodb支持的聚合表达式包括:

表达式说明举例说明
sum求和db.doc_demo.aggregate([{$group: {_id: "$name", size: {$sum: '$age'}}}])
avg平均值db.doc_demo.aggregate([{$group: {_id: "$name", size: {$avg: '$age'}}}])
min取最小db.doc_demo.aggregate([{$group: {_id: "$name", age: {$min: '$age'}}}])
max取最大db.doc_demo.aggregate([{$group: {_id: "$name", age: {$max: '$age'}}}])
push结果插入到一个数组中db.doc_demo.aggregate([{$group: {_id: "$name", age: {$push: '$age'}}}])
addToSet结果插入集合,过滤重复db.doc_demo.aggregate([{$group: {_id: "$name", age: {$addToSet: '$age'}}}])
first第一个db.doc_demo.aggregate([{$group: {_id: "$name", age: {$first: '$age'}}}])
last最后一个db.doc_demo.aggregate([{$group: {_id: "$name", age: {$last: '$age'}}}])

上面虽然介绍了分组支持的一些表达式,但是没有查询条件,难道只能针对所有的文档进行分组统计么?

分组过滤

借助$match来实现过滤统计,如下

db.doc_demo.aggregate([
  {$match: {'tag': {$gt: 1}}}, 
  {$group: {_id: '$name', age: {$sum: 1}}}
])

请注意,$match的语法规则和find的查询条件一样,会将满足条件的数据传递给后面的分组计算

这种方式和liux中的管道特别相似,aggregate方法的参数数组中,前面的执行完毕之后,将结果传递给后面的继续执行,除了$match$group之外,还有一些其他的操作

操作说明
$project修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
$match用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
$limit用来限制MongoDB聚合管道返回的文档数。
$skip在聚合管道中跳过指定数量的文档,并返回余下的文档。
$unwind将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
$group将集合中的文档分组,可用于统计结果。
$sort将输入文档排序后输出。
$geoNear输出接近某一地理位置的有序文档。
Loading...