MongoDB

1、文章概述

1.1 基本概念

MongoDB是一种非关系型数据库,数据最终存储为BSON(Binary JSON)类型。MongoDB包含三个重要逻辑概念:数据库、集合和文档,与关系型数据库相关概念映射如下图:
2022-07-07-08-38-54-073843.jpeg

1.2 数据准备

1.2.1 逻辑设计

bookdb数据库包含library与book两个集合,相关字段说明如下图:
2022-07-07-08-38-54-142779.jpeg

1.2.2 新增数据

  1. // 创建数据库
  2. use bookdb
  3. // 新增图书馆
  4. db.library.insertMany([
  5. {
  6. "_id": 1,
  7. "library_name": "图书馆_1"
  8. },
  9. {
  10. "_id": 2,
  11. "library_name": "图书馆_2"
  12. },
  13. {
  14. "_id": 3,
  15. "library_name": "图书馆_3"
  16. }
  17. ])
  18. // 新增图书
  19. db.book.insertMany([
  20. {
  21. "_id": 1,
  22. "book_type": 1,
  23. "book_name": "图书_1",
  24. "book_price": 10,
  25. "book_count": 100,
  26. "borrowers": [{
  27. "name": "张三",
  28. "id": 100
  29. }, {
  30. "name": "李四",
  31. "id": 200
  32. }],
  33. "library_id": 1
  34. },
  35. {
  36. "_id": 2,
  37. "book_type": 2,
  38. "book_name": "图书_2",
  39. "book_price": 20,
  40. "book_count": 100,
  41. "borrowers": [{
  42. "name": "张三",
  43. "id": 100
  44. }],
  45. "library_id": 2
  46. },
  47. {
  48. "_id": 3,
  49. "book_type": 1,
  50. "book_name": "图书_3",
  51. "book_price": 30,
  52. "book_count": 100,
  53. "borrowers": [{
  54. "name": "张三",
  55. "id": 100
  56. }, {
  57. "name": "王五",
  58. "id": 300
  59. }],
  60. "library_id": 2
  61. }
  62. ])

2、聚合概念

2.1 基本语法

MongoDB聚合语法第一个参数是管道:文档在上个管道处理完后传递给下个管道,第二个参数是选项:设置聚合操作特性

  1. db.collection.aggregate(pipeline, options)

2.2 聚合管道

常用聚合管道有以下九种类型:
$project:对文档进行投影
$limit:输出管道内前N个文档
$skip:跳过管道内前N个文档
$sort:对文档进行排序
$out:输出管道中文档
$match:对文档进行筛选
$unwind:铺平文档中的数组字段
$lookup:对文档进行查询
$group:对文档进行分组

3、聚合实例

3.1 project

1表示展示某字段
0表示不展示某字段
借阅人编号和姓名拆分成两个数组

  1. db.book.aggregate({
  2. $project: {
  3. "_id": 0,
  4. "book_name": 1,
  5. "borrowerIds": "$borrowers.id",
  6. "borrowerNames": "$borrowers.name"
  7. }
  8. })
  9. ---------------------------------------------------------
  10. // 1
  11. {
  12. "book_name": "图书_1",
  13. "borrowerIds": [
  14. 100,
  15. 200
  16. ],
  17. "borrowerNames": [
  18. "张三",
  19. "李四"
  20. ]
  21. }
  22. // 2
  23. {
  24. "book_name": "图书_2",
  25. "borrowerIds": [
  26. 100
  27. ],
  28. "borrowerNames": [
  29. "张三"
  30. ]
  31. }
  32. // 3
  33. {
  34. "book_name": "图书_3",
  35. "borrowerIds": [
  36. 100,
  37. 300
  38. ],
  39. "borrowerNames": [
  40. "张三",
  41. "王五"
  42. ]
  43. }

3.2 limit

只展示一个投影结果

  1. db.book.aggregate([
  2. {
  3. $project: {
  4. "_id": 0,
  5. "book_name": 1,
  6. "borrowerIds": "$borrowers.id",
  7. "borrowerNames": "$borrowers.name"
  8. }
  9. },
  10. {
  11. $limit: 1
  12. }
  13. ])
  14. ---------------------------------------------------------
  15. // 1
  16. {
  17. "book_name": "图书_1",
  18. "borrowerIds": [
  19. 100,
  20. 200
  21. ],
  22. "borrowerNames": [
  23. "张三",
  24. "李四"
  25. ]
  26. }

3.3 skip

跳过一个且只展示一个投影结果

  1. db.book.aggregate([
  2. {
  3. $project: {
  4. "_id": 0,
  5. "book_name": 1,
  6. "borrowerIds": "$borrowers.id",
  7. "borrowerNames": "$borrowers.name"
  8. }
  9. },
  10. {
  11. $skip: 1
  12. },
  13. {
  14. $limit: 1
  15. }
  16. ])
  17. ---------------------------------------------------------
  18. // 1
  19. {
  20. "book_name": "图书_2",
  21. "borrowerIds": [
  22. 100
  23. ],
  24. "borrowerNames": [
  25. "张三"
  26. ]
  27. }

3.4 sort

  1. db.book.aggregate([
  2. {
  3. $project: {
  4. "_id": 1,
  5. "book_name": 1,
  6. "library_id": 1
  7. }
  8. },
  9. {
  10. $sort: {
  11. "library_id": - 1, // 降序
  12. "_id": 1 // 升序
  13. }
  14. }
  15. ])
  16. ---------------------------------------------------------
  17. // 1
  18. {
  19. "_id": 2,
  20. "book_name": "图书_2",
  21. "library_id": 2
  22. }
  23. // 2
  24. {
  25. "_id": 3,
  26. "book_name": "图书_3",
  27. "library_id": 2
  28. }
  29. // 3
  30. {
  31. "_id": 1,
  32. "book_name": "图书_1",
  33. "library_id": 1
  34. }

MongoDB内存排序有100M限制,如果排序数据过多需要设置选项allowDiskUse=true,表示数据可以写入临时文件进行排序

  1. db.book.aggregate([
  2. {
  3. $project: {
  4. "_id": 1,
  5. "book_name": 1,
  6. "library_id": 1
  7. }
  8. },
  9. {
  10. $sort: {
  11. "library_id": - 1,
  12. "_id": 1
  13. }
  14. }
  15. ], {
  16. allowDiskUse: true
  17. })

3.5 out

投影结果输出到新集合

  1. db.book.aggregate([
  2. {
  3. $project: {
  4. "_id": 0,
  5. "book_name": 1,
  6. "borrowerIds": "$borrowers.id",
  7. "borrowerNames": "$borrowers.name"
  8. }
  9. },
  10. {
  11. $out: "newCollection"
  12. }
  13. ])
  14. db.newCollection.find()
  15. ---------------------------------------------------------
  16. // 1
  17. {
  18. "_id": ObjectId("62bec0636f9c37787b9590b9"),
  19. "book_name": "图书_1",
  20. "borrowerIds": [
  21. 100,
  22. 200
  23. ],
  24. "borrowerNames": [
  25. "张三",
  26. "李四"
  27. ]
  28. }
  29. // 2
  30. {
  31. "_id": ObjectId("62bec0636f9c37787b9590ba"),
  32. "book_name": "图书_2",
  33. "borrowerIds": [
  34. 100
  35. ],
  36. "borrowerNames": [
  37. "张三"
  38. ]
  39. }
  40. // 3
  41. {
  42. "_id": ObjectId("62bec0636f9c37787b9590bb"),
  43. "book_name": "图书_3",
  44. "borrowerIds": [
  45. 100,
  46. 300
  47. ],
  48. "borrowerNames": [
  49. "张三",
  50. "王五"
  51. ]
  52. }

3.6 match

where book_name = xxx

  1. db.book.aggregate([
  2. {
  3. $match: {
  4. "book_name": "图书_2"
  5. }
  6. },
  7. {
  8. $project: {
  9. "_id": 1,
  10. "book_name": 1,
  11. "library_id": 1
  12. }
  13. }
  14. ])
  15. ---------------------------------------------------------
  16. // 1
  17. {
  18. "_id": 2,
  19. "book_name": "图书_2",
  20. "library_id": 2
  21. }
  22. where library_id = 2 and price > 15
  23. db.book.aggregate([
  24. {
  25. $match: {
  26. $and: [
  27. {
  28. "library_id": 2
  29. },
  30. {
  31. "book_price": {
  32. $gt: 25
  33. }
  34. }
  35. ]
  36. }
  37. },
  38. {
  39. $project: {
  40. "_id": 1,
  41. "book_name": 1,
  42. "library_id": 1,
  43. "book_price": 1
  44. }
  45. }
  46. ])
  47. ---------------------------------------------------------
  48. // 1
  49. {
  50. "_id": 3,
  51. "book_name": "图书_3",
  52. "book_price": 30,
  53. "library_id": 2
  54. }

3.7 unwind

文档按照借阅人数组铺平
includeArrayIndex表示索引

  1. db.book.aggregate([
  2. {
  3. $unwind: {
  4. path: "$borrowers",
  5. includeArrayIndex: "idx"
  6. }
  7. }
  8. ])
  9. ---------------------------------------------------------
  10. // 1
  11. {
  12. "_id": 1,
  13. "book_type": 1,
  14. "book_name": "图书_1",
  15. "book_price": 10,
  16. "book_count": 100,
  17. "borrowers": {
  18. "name": "张三",
  19. "id": 100
  20. },
  21. "library_id": 1,
  22. "idx": NumberLong("0")
  23. }
  24. // 2
  25. {
  26. "_id": 1,
  27. "book_type": 1,
  28. "book_name": "图书_1",
  29. "book_price": 10,
  30. "book_count": 100,
  31. "borrowers": {
  32. "name": "李四",
  33. "id": 200
  34. },
  35. "library_id": 1,
  36. "idx": NumberLong("1")
  37. }
  38. // 3
  39. {
  40. "_id": 2,
  41. "book_type": 2,
  42. "book_name": "图书_2",
  43. "book_price": 20,
  44. "book_count": 100,
  45. "borrowers": {
  46. "name": "张三",
  47. "id": 100
  48. },
  49. "library_id": 2,
  50. "idx": NumberLong("0")
  51. }
  52. // 4
  53. {
  54. "_id": 3,
  55. "book_type": 1,
  56. "book_name": "图书_3",
  57. "book_price": 30,
  58. "book_count": 100,
  59. "borrowers": {
  60. "name": "张三",
  61. "id": 100
  62. },
  63. "library_id": 2,
  64. "idx": NumberLong("0")
  65. }
  66. // 5
  67. {
  68. "_id": 3,
  69. "book_type": 1,
  70. "book_name": "图书_3",
  71. "book_price": 30,
  72. "book_count": 100,
  73. "borrowers": {
  74. "name": "王五",
  75. "id": 300
  76. },
  77. "library_id": 2,
  78. "idx": NumberLong("1")
  79. }

3.8 lookup

查询图书馆有哪些图书
lookup可以实现连表查询
MongoDB 3.4之前聚合语法:

  • from:待关联集合【book】
  • localField: 本集合关联键【library】
  • foreignField:待关联键【book】
  • as:待关联集合数据【book】 ```sql db.library.aggregate([ {
    1. $lookup:
    2. {
    3. from: "book",
    4. localField: "_id",
    5. foreignField: "library_id",
    6. as: "books_info"
    7. }
    } ])

// 1 { “_id”: 1, “library_name”: “图书馆_1”, “books_info”: [ { “_id”: 1, “book_type”: 1, “book_name”: “图书_1”, “book_price”: 10, “book_count”: 100, “borrowers”: [ { “name”: “张三”, “id”: 100 }, { “name”: “李四”, “id”: 200 } ], “library_id”: 1 } ] }

// 2 { “_id”: 2, “library_name”: “图书馆_2”, “books_info”: [ { “_id”: 2, “book_type”: 2, “book_name”: “图书_2”, “book_price”: 20, “book_count”: 100, “borrowers”: [ { “name”: “张三”, “id”: 100 } ], “library_id”: 2 }, { “_id”: 3, “book_type”: 1, “book_name”: “图书_3”, “book_price”: 30, “book_count”: 100, “borrowers”: [ { “name”: “张三”, “id”: 100 }, { “name”: “王五”, “id”: 300 } ], “library_id”: 2 } ] }

// 3 { “_id”: 3, “library_name”: “图书馆_3”, “books_info”: [ ] }

  1. MongoDB 3.4之后聚合语法:
  2. - **from:待关联集合【book】**
  3. - **let:声明本集合字段在管道使用**
  4. - **pipeline:操作管道**
  5. ```sql
  6. db.library.aggregate([
  7. {
  8. $lookup:
  9. {
  10. from: "book",
  11. let: {
  12. "lid": "$_id"
  13. },
  14. pipeline: [
  15. {
  16. $match: {
  17. $expr: {
  18. $and: [
  19. {
  20. $eq: ["$$lid", "$library_id"]
  21. }
  22. ]
  23. }
  24. }
  25. }
  26. ],
  27. as: "books_info"
  28. }
  29. }
  30. ])
  31. ---------------------------------------------------------
  32. // 1
  33. {
  34. "_id": 1,
  35. "library_name": "图书馆_1",
  36. "books_info": [
  37. {
  38. "_id": 1,
  39. "book_type": 1,
  40. "book_name": "图书_1",
  41. "book_price": 10,
  42. "book_count": 100,
  43. "borrowers": [
  44. {
  45. "name": "张三",
  46. "id": 100
  47. },
  48. {
  49. "name": "李四",
  50. "id": 200
  51. }
  52. ],
  53. "library_id": 1
  54. }
  55. ]
  56. }
  57. // 2
  58. {
  59. "_id": 2,
  60. "library_name": "图书馆_2",
  61. "books_info": [
  62. {
  63. "_id": 2,
  64. "book_type": 2,
  65. "book_name": "图书_2",
  66. "book_price": 20,
  67. "book_count": 100,
  68. "borrowers": [
  69. {
  70. "name": "张三",
  71. "id": 100
  72. }
  73. ],
  74. "library_id": 2
  75. },
  76. {
  77. "_id": 3,
  78. "book_type": 1,
  79. "book_name": "图书_3",
  80. "book_price": 30,
  81. "book_count": 100,
  82. "borrowers": [
  83. {
  84. "name": "张三",
  85. "id": 100
  86. },
  87. {
  88. "name": "王五",
  89. "id": 300
  90. }
  91. ],
  92. "library_id": 2
  93. }
  94. ]
  95. }
  96. // 3
  97. {
  98. "_id": 3,
  99. "library_name": "图书馆_3",
  100. "books_info": [ ]
  101. }

新增价格大于20查询条件

  1. db.library.aggregate([
  2. {
  3. $lookup:
  4. {
  5. from: "book",
  6. let: {
  7. "lid": "$_id"
  8. },
  9. pipeline: [
  10. {
  11. $match: {
  12. $expr: {
  13. $and: [
  14. {
  15. $eq: ["$$lid", "$library_id"]
  16. },
  17. {
  18. $gt: ["$book_price", 20]
  19. }
  20. ]
  21. }
  22. }
  23. }
  24. ],
  25. as: "books_info"
  26. }
  27. }
  28. ])
  29. ---------------------------------------------------------
  30. // 1
  31. {
  32. "_id": 1,
  33. "library_name": "图书馆_1",
  34. "books_info": [ ]
  35. }
  36. // 2
  37. {
  38. "_id": 2,
  39. "library_name": "图书馆_2",
  40. "books_info": [
  41. {
  42. "_id": 3,
  43. "book_type": 1,
  44. "book_name": "图书_3",
  45. "book_price": 30,
  46. "book_count": 100,
  47. "borrowers": [
  48. {
  49. "name": "张三",
  50. "id": 100
  51. },
  52. {
  53. "name": "王五",
  54. "id": 300
  55. }
  56. ],
  57. "library_id": 2
  58. }
  59. ]
  60. }
  61. // 3
  62. {
  63. "_id": 3,
  64. "library_name": "图书馆_3",
  65. "books_info": [ ]
  66. }

3.9 group

3.9.1 简单统计

_id:图书类型作为分组键
count:每个类型有多少种书

  1. db.book.aggregate([
  2. {
  3. $group: {
  4. _id: "$book_type",
  5. count: {
  6. $sum: 1
  7. }
  8. }
  9. }
  10. ])
  11. ---------------------------------------------------------
  12. // 1
  13. {
  14. "_id": 2,
  15. "count": 1
  16. }
  17. // 2
  18. {
  19. "_id": 1,
  20. "count": 2
  21. }

3.9.2 复杂统计

_id:图书类型作为分组键
type_count:每个类型有多少种书
type_book_count:每个类型有多少本书
minTotalPrice:每个类型总价最小值
maxTotalPrice:每个类型总价最大值
totalPrice:每个类型总价
avgPrice:每个类型平均价

  1. db.book.aggregate([
  2. {
  3. $group: {
  4. _id: "$book_type",
  5. type_count: {
  6. $sum: 1
  7. },
  8. type_book_count: {
  9. $sum: "$book_count"
  10. },
  11. minTotalPrice: {
  12. $min: {
  13. $multiply: ["$book_price", "$book_count"]
  14. }
  15. },
  16. maxTotalPrice: {
  17. $max: {
  18. $multiply: ["$book_price", "$book_count"]
  19. }
  20. },
  21. totalPrice: {
  22. $sum: {
  23. $multiply: ["$book_price", "$book_count"]
  24. }
  25. },
  26. avgPrice: {
  27. $avg: "$book_price"
  28. }
  29. }
  30. }
  31. ])
  32. ---------------------------------------------------------
  33. // 1
  34. {
  35. "_id": 2,
  36. "type_count": 1,
  37. "type_book_count": 100,
  38. "minTotalPrice": 2000,
  39. "maxTotalPrice": 2000,
  40. "totalPrice": 2000,
  41. "avgPrice": 20
  42. }
  43. // 2
  44. {
  45. "_id": 1,
  46. "type_count": 2,
  47. "type_book_count": 200,
  48. "minTotalPrice": 1000,
  49. "maxTotalPrice": 3000,
  50. "totalPrice": 4000,
  51. "avgPrice": 20
  52. }

3.9.3 空分组键

_id:空分组键表示统计全量数据

  1. db.book.aggregate([
  2. {
  3. $group: {
  4. _id: null,
  5. type_count: {
  6. $sum: 1
  7. },
  8. type_book_count: {
  9. $sum: "$book_count"
  10. },
  11. minTotalPrice: {
  12. $min: {
  13. $multiply: ["$book_price", "$book_count"]
  14. }
  15. },
  16. maxTotalPrice: {
  17. $max: {
  18. $multiply: ["$book_price", "$book_count"]
  19. }
  20. },
  21. totalPrice: {
  22. $sum: {
  23. $multiply: ["$book_price", "$book_count"]
  24. }
  25. },
  26. avgPrice: {
  27. $avg: "$book_price"
  28. }
  29. }
  30. }
  31. ])
  32. ---------------------------------------------------------
  33. // 1
  34. {
  35. "_id": null,
  36. "type_count": 3,
  37. "type_book_count": 300,
  38. "minTotalPrice": 1000,
  39. "maxTotalPrice": 3000,
  40. "totalPrice": 6000,
  41. "avgPrice": 20
  42. }

4 、总结

第一介绍了MongoDB与关系型数据库关系,并且准备本文需要的测试数据,第二介绍了聚合语法和聚合管道相关概念,第三通过实例介绍了如何使用聚合操作。