mongodb-18.聚合查询练习1

bulk write

db.pizzas.insertMany( [
   { _id: 0, type: "pepperoni", size: "small", price: 4 },
   { _id: 1, type: "cheese", size: "medium", price: 7 },
   { _id: 2, type: "vegan", size: "large", price: 8 }
] )
try {
   db.pizzas.bulkWrite( [
      { insertOne: { document: { _id: 3, type: "beef", size: "medium", price: 6 } } },
      { insertOne: { document: { _id: 4, type: "sausage", size: "large", price: 10 } } },
      { updateOne: {
         filter: { type: "cheese" },
         update: { $set: { price: 8 } }
      } },
      { deleteOne: { filter: { type: "pepperoni"} } },
      { replaceOne: {
         filter: { type: "vegan" },
         replacement: { type: "tofu", size: "small", price: 4 }
      } }
   ] )
} catch( error ) {
   print( error )
}

输出

{
   acknowledged: true,
   insertedCount: 2,
   insertedIds: { '0': 3, '1': 4 },
   matchedCount: 2,
   modifiedCount: 2,
   deletedCount: 1,
   upsertedCount: 0,
   upsertedIds: {}
}

addFields

增加field

db.scores.insertMany([
  {
  _id: 1,
  student: "Maya",
  homework: [ 10, 5, 10 ],
  quiz: [ 10, 8 ],
  extraCredit: 0
},
{
  _id: 2,
  student: "Ryan",
  homework: [ 5, 6, 5 ],
  quiz: [ 8, 8 ],
  extraCredit: 8
}])
db.scores.aggregate([
  {
    $addFields:{
      totalHomework:{$sum:"$homework"},
      totalQuiz:{$sum:"$quiz"}
    }
  },
  {
    $addFields:{
      totalSocre:{$add:["$totalHomework","$totalQuiz","$extractCredit"]}
    }
  }
  ])

结果

{ _id: 1,
  student: 'Maya',
  homework: [ 10, 5, 10 ],
  quiz: [ 10, 8 ],
  extraCredit: 0,
  totalHomework: 25,
  totalQuiz: 18,
  totalSocre: null }
{ _id: 2,
  student: 'Ryan',
  homework: [ 5, 6, 5 ],
  quiz: [ 8, 8 ],
  extraCredit: 8,
  totalHomework: 16,
  totalQuiz: 16,
  totalSocre: null }

嵌套增加field

db.vehicles.insertMany(
   [
      { _id: 1, type: "car", specs: { doors: 4, wheels: 4 } },
      { _id: 2, type: "motorcycle", specs: { doors: 0, wheels: 2 } },
      { _id: 3, type: "jet ski" }
   ]
)
db.vehicles.aggregate( [
        {
           $addFields: {
              "specs.fuel_type": "unleaded"
           }
        }
   ] )
{ _id: 1, type: "car",
   specs: { doors: 4, wheels: 4, fuel_type: "unleaded" } }
{ _id: 2, type: "motorcycle",
   specs: { doors: 0, wheels: 2, fuel_type: "unleaded" } }
{ _id: 3, type: "jet ski",
   specs: { fuel_type: "unleaded" } }

覆盖显示

db.scores.aggregate({
  $addFields:{extraCredit:0}
})


{_id: 1,
  student: 'Maya',
  homework: [ 10, 5, 10 ],
  quiz: [ 10, 8 ],
  extraCredit: 0 }
{ _id: 2,
  student: 'Ryan',
  homework: [ 5, 6, 5 ],
  quiz: [ 8, 8 ],
  extraCredit: 0 }

用变量替换

db.scores.aggregate({
  $addFields:{_id:"$item",item:"test"}
  
})
{ student: 'Maya',
  homework: [ 10, 5, 10 ],
  quiz: [ 10, 8 ],
  extraCredit: 0,
  item: 'test' }
{ student: 'Ryan',
  homework: [ 5, 6, 5 ],
  quiz: [ 8, 8 ],
  extraCredit: 8,
  item: 'test' }

向数组中增加元素

db.scores.insertMany([
   { _id: 1, student: "Maya", homework: [ 10, 5, 10 ], quiz: [ 10, 8 ], extraCredit: 0 },
   { _id: 2, student: "Ryan", homework: [ 5, 6, 5 ], quiz: [ 8, 8 ], extraCredit: 8 }
])
db.scores.aggregate([
   { $match: { _id: 1 } },
   { $addFields: { homework: { $concatArrays: [ "$homework", [ 7 ] ] } } }
])
{ "_id" : 1, "student" : "Maya", "homework" : [ 10, 5, 10, 7 ], "quiz" : [ 10, 8 ], "extraCredit" : 0 }

分组 bucket

$bucket and Memory Restrictions The $bucket stage has a limit of 100 megabytes of RAM. By default, if the stage exceeds this limit, $bucket returns an error. To allow more space for stage processing, use the allowDiskUse option to enable aggregation pipeline stages to write data to temporary files.

{
  $bucket: {
      groupBy: <expression>,
      boundaries: [ <lowerbound1>, <lowerbound2>, ... ],
      default: <literal>,
      output: {
         <output1>: { <$accumulator expression> },
         ...
         <outputN>: { <$accumulator expression> }
      }
   }
}

db.artists.insertMany([
  { "_id" : 1, "last_name" : "Bernard", "first_name" : "Emil", "year_born" : 1868, "year_died" : 1941, "nationality" : "France" },
  { "_id" : 2, "last_name" : "Rippl-Ronai", "first_name" : "Joszef", "year_born" : 1861, "year_died" : 1927, "nationality" : "Hungary" },
  { "_id" : 3, "last_name" : "Ostroumova", "first_name" : "Anna", "year_born" : 1871, "year_died" : 1955, "nationality" : "Russia" },
  { "_id" : 4, "last_name" : "Van Gogh", "first_name" : "Vincent", "year_born" : 1853, "year_died" : 1890, "nationality" : "Holland" },
  { "_id" : 5, "last_name" : "Maurer", "first_name" : "Alfred", "year_born" : 1868, "year_died" : 1932, "nationality" : "USA" },
  { "_id" : 6, "last_name" : "Munch", "first_name" : "Edvard", "year_born" : 1863, "year_died" : 1944, "nationality" : "Norway" },
  { "_id" : 7, "last_name" : "Redon", "first_name" : "Odilon", "year_born" : 1840, "year_died" : 1916, "nationality" : "France" },
  { "_id" : 8, "last_name" : "Diriks", "first_name" : "Edvard", "year_born" : 1855, "year_died" : 1930, "nationality" : "Norway" }
])
db.artists.aggregate( [
  // First Stage
  {
    $bucket: {
      groupBy: "$year_born",                        // Field to group by
      boundaries: [ 1840, 1850, 1860, 1870, 1880 ], // Boundaries for the buckets
      default: "Other",                             // Bucket ID for documents which do not fall into a bucket
      output: {                                     // Output for each bucket
        "count": { $sum: 1 },
        "artists" :
          {
            $push: {
              "name": { $concat: [ "$first_name", " ", "$last_name"] },
              "year_born": "$year_born"
            }
          }
      }
    }
  },
  // Second Stage
  {
    $match: { count: {$gt: 3} }
  }
] )

备注

The 
$bucket
 stage groups the documents into buckets by the year_born field. The buckets have the following 
boundaries:

[1840, 1850)

[1850, 1860)

[1860, 1870)

[1870, 1880) 


If a document did not contain the year_born field or its year_born field was outside the ranges above, it would be placed in the default bucket with the _id value "Other".

所以第一个stage产出的数据是

{ _id: 1840,
  count: 1,
  artists: [ { name: 'OdilonRedon', year_born: 1840 } ] }
{ _id: 1850,
  count: 2,
  artists: 
   [ { name: 'VincentVan Gogh', year_born: 1853 },
     { name: 'EdvardDiriks', year_born: 1855 } ] }
{ _id: 1860,
  count: 4,
  artists: 
   [ { name: 'EmilBernard', year_born: 1868 },
     { name: 'JoszefRippl-Ronai', year_born: 1861 },
     { name: 'AlfredMaurer', year_born: 1868 },
     { name: 'EdvardMunch', year_born: 1863 } ] }
{ _id: 1870,
  count: 1,
  artists: [ { name: 'AnnaOstroumova', year_born: 1871 } ] }

然后根据第二个stage 的$match进行过滤,得到

{ _id: 1860,
  count: 4,
  artists: 
   [ { name: 'EmilBernard', year_born: 1868 },
     { name: 'JoszefRippl-Ronai', year_born: 1861 },
     { name: 'AlfredMaurer', year_born: 1868 },
     { name: 'EdvardMunch', year_born: 1863 } ] }

并行执行多个bucket

db.artwork.insertMany([
  { "_id" : 1, "title" : "The Pillars of Society", "artist" : "Grosz", "year" : 1926,
      "price" : NumberDecimal("199.99") },
  { "_id" : 2, "title" : "Melancholy III", "artist" : "Munch", "year" : 1902,
      "price" : NumberDecimal("280.00") },
  { "_id" : 3, "title" : "Dancer", "artist" : "Miro", "year" : 1925,
      "price" : NumberDecimal("76.04") },
  { "_id" : 4, "title" : "The Great Wave off Kanagawa", "artist" : "Hokusai",
      "price" : NumberDecimal("167.30") },
  { "_id" : 5, "title" : "The Persistence of Memory", "artist" : "Dali", "year" : 1931,
      "price" : NumberDecimal("483.00") },
  { "_id" : 6, "title" : "Composition VII", "artist" : "Kandinsky", "year" : 1913,
      "price" : NumberDecimal("385.00") },
  { "_id" : 7, "title" : "The Scream", "artist" : "Munch", "year" : 1893
      /* No price*/ },
  { "_id" : 8, "title" : "Blue Flower", "artist" : "O'Keefe", "year" : 1918,
      "price" : NumberDecimal("118.42") }
])
db.artwork.aggregate( [
  {
    $facet: {                               // Top-level $facet stage
      "price": [                            // Output field 1
        {
          $bucket: {
              groupBy: "$price",            // Field to group by
              boundaries: [ 0, 200, 400 ],  // Boundaries for the buckets
              default: "Other",             // Bucket ID for documents which do not fall into a bucket
              output: {                     // Output for each bucket
                "count": { $sum: 1 },
                "artwork" : { $push: { "title": "$title", "price": "$price" } },
                "averagePrice": { $avg: "$price" }
              }
          }
        }
      ],
      "year": [                                      // Output field 2
        {
          $bucket: {
            groupBy: "$year",                        // Field to group by
            boundaries: [ 1890, 1910, 1920, 1940 ],  // Boundaries for the buckets
            default: "Unknown",                      // Bucket ID for documents which do not fall into a bucket
            output: {                                // Output for each bucket
              "count": { $sum: 1 },
              "artwork": { $push: { "title": "$title", "year": "$year" } }
            }
          }
        }
      ]
    }
  }
] )

产出

{ price: 
   [ { _id: 0,
       count: 4,
       artwork: 
        [ { title: 'The Pillars of Society', price: Decimal128("199.99") },
          { title: 'Dancer', price: Decimal128("76.04") },
          { title: 'The Great Wave off Kanagawa',
            price: Decimal128("167.30") },
          { title: 'Blue Flower', price: Decimal128("118.42") } ],
       averagePrice: Decimal128("140.4375") },
     { _id: 200,
       count: 2,
       artwork: 
        [ { title: 'Melancholy III', price: Decimal128("280.00") },
          { title: 'Composition VII', price: Decimal128("385.00") } ],
       averagePrice: Decimal128("332.50") },
     { _id: 'Other',
       count: 2,
       artwork: 
        [ { title: 'The Persistence of Memory',
            price: Decimal128("483.00") },
          { title: 'The Scream' } ],
       averagePrice: Decimal128("483.00") } ],
  year: 
   [ { _id: 1890,
       count: 2,
       artwork: 
        [ { title: 'Melancholy III', year: 1902 },
          { title: 'The Scream', year: 1893 } ] },
     { _id: 1910,
       count: 2,
       artwork: 
        [ { title: 'Composition VII', year: 1913 },
          { title: 'Blue Flower', year: 1918 } ] },
     { _id: 1920,
       count: 3,
       artwork: 
        [ { title: 'The Pillars of Society', year: 1926 },
          { title: 'Dancer', year: 1925 },
          { title: 'The Persistence of Memory', year: 1931 } ] },
     { _id: 'Unknown',
       count: 1,
       artwork: [ { title: 'The Great Wave off Kanagawa' } ] } ] }

$bucketAuto

db.artwork.insertMany([
  { "_id" : 1, "title" : "The Pillars of Society", "artist" : "Grosz", "year" : 1926,
    "price" : NumberDecimal("199.99"),
    "dimensions" : { "height" : 39, "width" : 21, "units" : "in" } },
{ "_id" : 2, "title" : "Melancholy III", "artist" : "Munch", "year" : 1902,
    "price" : NumberDecimal("280.00"),
    "dimensions" : { "height" : 49, "width" : 32, "units" : "in" } },
{ "_id" : 3, "title" : "Dancer", "artist" : "Miro", "year" : 1925,
    "price" : NumberDecimal("76.04"),
    "dimensions" : { "height" : 25, "width" : 20, "units" : "in" } },
{ "_id" : 4, "title" : "The Great Wave off Kanagawa", "artist" : "Hokusai",
    "price" : NumberDecimal("167.30"),
    "dimensions" : { "height" : 24, "width" : 36, "units" : "in" } },
{ "_id" : 5, "title" : "The Persistence of Memory", "artist" : "Dali", "year" : 1931,
    "price" : NumberDecimal("483.00"),
    "dimensions" : { "height" : 20, "width" : 24, "units" : "in" } },
{ "_id" : 6, "title" : "Composition VII", "artist" : "Kandinsky", "year" : 1913,
    "price" : NumberDecimal("385.00"),
    "dimensions" : { "height" : 30, "width" : 46, "units" : "in" } },
{ "_id" : 7, "title" : "The Scream", "artist" : "Munch",
    "price" : NumberDecimal("159.00"),
    "dimensions" : { "height" : 24, "width" : 18, "units" : "in" } },
{ "_id" : 8, "title" : "Blue Flower", "artist" : "O'Keefe", "year" : 1918,
    "price" : NumberDecimal("118.42"),
    "dimensions" : { "height" : 24, "width" : 20, "units" : "in" } }])
{ acknowledged: true,
  insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8 } }
db.artwork.aggregate([
  {
    $bucketAuto:{
      groupBy:"$price",
      buckets:4
    }
  }
  ])
{ _id: { min: Decimal128("76.04"), max: Decimal128("159.00") },
  count: 2 }
{ _id: { min: Decimal128("159.00"), max: Decimal128("199.99") },
  count: 2 }
{ _id: { min: Decimal128("199.99"), max: Decimal128("385.00") },
  count: 2 }
{ _id: { min: Decimal128("385.00"), max: Decimal128("483.00") },
  count: 2 }
db.artwork.aggregate( [
  {
    $facet: {
      "price": [
        {
          $bucketAuto: {
            groupBy: "$price",
            buckets: 4
          }
        }
      ],
      "year": [
        {
          $bucketAuto: {
            groupBy: "$year",
            buckets: 3,
            output: {
              "count": { $sum: 1 },
              "years": { $push: "$year" }
            }
          }
        }
      ],
      "area": [
        {
          $bucketAuto: {
            groupBy: {
              $multiply: [ "$dimensions.height", "$dimensions.width" ]
            },
            buckets: 4,
            output: {
              "count": { $sum: 1 },
              "titles": { $push: "$title" }
            }
          }
        }
      ]
    }
  }
] )

输出

{
  "area" : [
    {
      "_id" : { "min" : 432, "max" : 500 },
      "count" : 3,
      "titles" : [
        "The Scream",
        "The Persistence of Memory",
        "Blue Flower"
      ]
    },
    {
      "_id" : { "min" : 500, "max" : 864 },
      "count" : 2,
      "titles" : [
        "Dancer",
        "The Pillars of Society"
      ]
    },
    {
      "_id" : { "min" : 864, "max" : 1568 },
      "count" : 2,
      "titles" : [
        "The Great Wave off Kanagawa",
        "Composition VII"
      ]
    },
    {
      "_id" : { "min" : 1568, "max" : 1568 },
      "count" : 1,
      "titles" : [
        "Melancholy III"
      ]
    }
  ],
  "price" : [
    {
      "_id" : { "min" : NumberDecimal("76.04"), "max" : NumberDecimal("159.00") },
      "count" : 2
    },
    {
      "_id" : { "min" : NumberDecimal("159.00"), "max" : NumberDecimal("199.99") },
      "count" : 2
    },
    {
      "_id" : { "min" : NumberDecimal("199.99"), "max" : NumberDecimal("385.00") },
      "count" : 2 },
    {
      "_id" : { "min" : NumberDecimal("385.00"), "max" : NumberDecimal("483.00") },
      "count" : 2
    }
  ],
  "year" : [
    { "_id" : { "min" : null, "max" : 1913 }, "count" : 3, "years" : [ 1902 ] },
    { "_id" : { "min" : 1913, "max" : 1926 }, "count" : 3, "years" : [ 1913, 1918, 1925 ] },
    { "_id" : { "min" : 1926, "max" : 1931 }, "count" : 2, "years" : [ 1926, 1931 ] }
  ]
}

$count

db.scores.insertMany([
   { "_id" : 1, "subject" : "History", "score" : 88 },
{ "_id" : 2, "subject" : "History", "score" : 92 },
{ "_id" : 3, "subject" : "History", "score" : 97 },
{ "_id" : 4, "subject" : "History", "score" : 71 },
{ "_id" : 5, "subject" : "History", "score" : 79 },
{ "_id" : 6, "subject" : "History", "score" : 83 }
   ])
{ acknowledged: true,
  insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6 } }
db.scores.aggregate([
  {
    $match:{
      score:{$gt:80}
    }
  }
  ])
{ _id: 1, subject: 'History', score: 88 }
{ _id: 2, subject: 'History', score: 92 }
{ _id: 3, subject: 'History', score: 97 }
{ _id: 6, subject: 'History', score: 83 }

db.scores.aggregate([
  {
    $match:{
      score:{$gt:80}
    }
  },{
    $count:"passing_scores"
  }
  ])
{ passing_scores: 4 }

$document

db.aggregate(
   [
      { $documents: [ { x: 10 }, { x: 2 }, { x: 5 } ] },
      { $bucketAuto: { groupBy: "$x", buckets: 4 } }
   ]
)
{ _id: { min: 2, max: 5 }, count: 1 }
{ _id: { min: 5, max: 10 }, count: 1 }
{ _id: { min: 10, max: 10 }, count: 1 }

$facet

facet可以实现在facet管道操作完成多个stage管道操作。减少获取输入文档的次数。

分页查询文档数据的同时,把符合查询条件的总数也查询出来的场景下,如果使用$facet,同时获取分页数据和总数,不用做两次数据库查询(分别查询分页数据和总数)。

$unwind 学习

db.user.insertMany([
  {
    user_id:"A_id" ,
    bonus:[
        { type:"a" ,amount:1000 },
        { type:"b" ,amount:2000 },
        { type:"b" ,amount:3000 }
    ]
}
  ])
{ acknowledged: true,
  insertedIds: { '0': ObjectId("63b675e5e13faeb56183402e") } }

使用$unwind进行拆分

db.user.aggregate([
    {$unwind:"$bonus"}
])
{ _id: ObjectId("63b675e5e13faeb56183402e"),
  user_id: 'A_id',
  bonus: { type: 'a', amount: 1000 } }
{ _id: ObjectId("63b675e5e13faeb56183402e"),
  user_id: 'A_id',
  bonus: { type: 'b', amount: 2000 } }
{ _id: ObjectId("63b675e5e13faeb56183402e"),
  user_id: 'A_id',
  bonus: { type: 'b', amount: 3000 } }

结合统计一起查询

db.user.aggregate([
  {$match:{user_id:"A_id"}},
  {$unwind:"$bonus"},
  {$match:{"bonus.type":"b"}},
  {$group:{_id:"$user_id",amount:{$sum:"$bonus.amount"}}}
  ])
{ _id: 'A_id', amount: 5000 }

db.artwork.insertMany([
  { "_id" : 1, "title" : "The Pillars of Society", "artist" : "Grosz", "year" : 1926,
  "price" : NumberDecimal("199.99"),
  "tags" : [ "painting", "satire", "Expressionism", "caricature" ] },
{ "_id" : 2, "title" : "Melancholy III", "artist" : "Munch", "year" : 1902,
  "price" : NumberDecimal("280.00"),
  "tags" : [ "woodcut", "Expressionism" ] },
{ "_id" : 3, "title" : "Dancer", "artist" : "Miro", "year" : 1925,
  "price" : NumberDecimal("76.04"),
  "tags" : [ "oil", "Surrealism", "painting" ] },
{ "_id" : 4, "title" : "The Great Wave off Kanagawa", "artist" : "Hokusai",
  "price" : NumberDecimal("167.30"),
  "tags" : [ "woodblock", "ukiyo-e" ] },
{ "_id" : 5, "title" : "The Persistence of Memory", "artist" : "Dali", "year" : 1931,
  "price" : NumberDecimal("483.00"),
  "tags" : [ "Surrealism", "painting", "oil" ] },
{ "_id" : 6, "title" : "Composition VII", "artist" : "Kandinsky", "year" : 1913,
  "price" : NumberDecimal("385.00"),
  "tags" : [ "oil", "painting", "abstract" ] },
{ "_id" : 7, "title" : "The Scream", "artist" : "Munch", "year" : 1893,
  "tags" : [ "Expressionism", "painting", "oil" ] },
{ "_id" : 8, "title" : "Blue Flower", "artist" : "O'Keefe", "year" : 1918,
  "price" : NumberDecimal("118.42"),
  "tags" : [ "abstract", "painting" ] }
  ])
{ acknowledged: true,
  insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5, '5': 6, '6': 7, '7': 8 } }

按照tag分类:先用unwind拆分tags字段的数组值,交给下一个聚合 $sortByCount, 按照tags的个数排序

db.artwork.aggregate([
  {$unwind:"$tags"},
  {$sortByCount:"$tags"}
 
  ])
{ _id: 'painting', count: 6 }
{ _id: 'oil', count: 4 }
{ _id: 'Expressionism', count: 3 }
{ _id: 'Surrealism', count: 2 }
{ _id: 'abstract', count: 2 }
{ _id: 'ukiyo-e', count: 1 }
{ _id: 'woodblock', count: 1 }
{ _id: 'woodcut', count: 1 }
{ _id: 'satire', count: 1 }
{ _id: 'caricature', count: 1 }

按照price分类:先过滤数据(只处理存在price数据的文档),然后执行$bucket按照价格区间分组 0150,151200, 201~300, 301~400这样。

db.artwork.aggregate([
  {$match:{price:{$exists:1}}},
  {$bucket:{
    groupBy:"$price",
    boundaries:[0,150,200,300,400],
    default:"Other"
  }}
  ])
{ _id: 0, count: 2 }
{ _id: 150, count: 2 }
{ _id: 200, count: 1 }
{ _id: 300, count: 1 }
{ _id: 'Other', count: 1 }

进一步设置输出字段

db.artwork.aggregate([
  {$match:{price:{$exists:1}}},
  {$bucket:{
    groupBy:"$price",
    boundaries:[0,150,200,300,400],
    default:"Other"
  }}
  ])
{ _id: 0, count: 2 }
{ _id: 150, count: 2 }
{ _id: 200, count: 1 }
{ _id: 300, count: 1 }
{ _id: 'Other', count: 1 }

按照years分类。 分成4个区间。

db.artwork.aggregate([
  {$bucketAuto:{
    groupBy:"$year",
    buckets:4
    
  }}
  ])
{ _id: { min: null, max: 1902 }, count: 2 }
{ _id: { min: 1902, max: 1918 }, count: 2 }
{ _id: { min: 1918, max: 1926 }, count: 2 }
{ _id: { min: 1926, max: 1931 }, count: 2 }

整合到一起就是

db.artwork.aggregate([
  {
    $facet:{
      "categorizedByTags":[
        {$unwind:"$tags"},
        {$sortByCount:"$tags"}
        ],
      "categorizedByPrice":[
        {$match:{price:{$exists:1}}},
        {$bucket:{
          groupBy:"$price",
          boundaries:[0,150,200,300,400],
          default:"Other",
          output:{
            "count":{$sum:1},
            "titles":{$push:"$title"}
          }
        }}
        ]  ,
        "categorizedByYears(Auto)":[
          {$bucketAuto:{
            groupBy:"$year",
            buckets:4
          }}
          ]
    }
  }
  ])
{ categorizedByTags: 
   [ { _id: 'painting', count: 6 },
     { _id: 'oil', count: 4 },
     { _id: 'Expressionism', count: 3 },
     { _id: 'Surrealism', count: 2 },
     { _id: 'abstract', count: 2 },
     { _id: 'woodcut', count: 1 },
     { _id: 'woodblock', count: 1 },
     { _id: 'ukiyo-e', count: 1 },
     { _id: 'caricature', count: 1 },
     { _id: 'satire', count: 1 } ],
  categorizedByPrice: 
   [ { _id: 0, count: 2, titles: [ 'Dancer', 'Blue Flower' ] },
     { _id: 150,
       count: 2,
       titles: [ 'The Pillars of Society', 'The Great Wave off Kanagawa' ] },
     { _id: 200, count: 1, titles: [ 'Melancholy III' ] },
     { _id: 300, count: 1, titles: [ 'Composition VII' ] },
     { _id: 'Other',
       count: 1,
       titles: [ 'The Persistence of Memory' ] } ],
  'categorizedByYears(Auto)': 
   [ { _id: { min: null, max: 1902 }, count: 2 },
     { _id: { min: 1902, max: 1918 }, count: 2 },
     { _id: { min: 1918, max: 1926 }, count: 2 },
     { _id: { min: 1926, max: 1931 }, count: 2 } ] }
1.使用Aggregation对象实现

    @Test
    public void testFacetAggregations(){
        String ARTWORK_COLLECTION = "artwork";
        // Facet中第一组分类(categorizedByTags)的两个聚合操作unwind 和 sortByCount
        UnwindOperation unwindForByTags = Aggregation.unwind("$tags");
        SortByCountOperation sortByCountForByTags = Aggregation.sortByCount("$tags");

        // Facet中第二组分类(categorizedByPrice)的聚合操作match 和 match
        MatchOperation matchForByPrice = Aggregation.match(Criteria.where("price").exists(true));
        // 分别传入bucket分组的字段price,设置区间值,并设置桶内条数统计和值(这里用titles接收title的值)
        BucketOperation bucketForByPrice = Aggregation.bucket("$price")
                .withBoundaries(0, 150, 200, 300, 400)
                .withDefaultBucket("Other")
                .andOutput("count").sum(1).as("count")
                .andOutput("$title").push().as("titles");

        // Facet中第三组分类 (categorizedByYears(Auto))的聚合操作,按年自动分成4个区间。
        BucketAutoOperation bucketForByYears = Aggregation.bucketAuto("$year", 4);

        // Aggregation调用facet方法,按照组别分类顺序,把每一组的聚合操作和输出的名称传进去。
        FacetOperation facetOperation = Aggregation.facet(unwindForByTags, sortByCountForByTags).as("categorizedByTags")
                .and(matchForByPrice, bucketForByPrice).as("categorizedByPrice")
                .and(bucketForByYears).as("categorizedByYears(Auto)");
        // 把facetOperation传入newAggregation得到Aggregation对象,调用mongoTemplate的Aggregate方法执行得到结果
        Aggregation aggregation = Aggregation.newAggregation(facetOperation);
        AggregationResults<Document> resultList = mongoTemplate.aggregate(aggregation, ARTWORK_COLLECTION, Document.class);
        for (Document document : resultList) {
            System.out.println("result is :" + document);
        }
    }
2.使用Aggregates实现

    @Test
    public void testFacetAggregates() {
        String ARTWORK_COLLECTION = "artwork";
        // Facet中第一组分类(categorizedByTags)的两个聚合操作unwind 和 sortByCount
        Bson unwindBsonForByTags = Aggregates.unwind("$tags");
        Bson sortByCountBsonForByTags = Aggregates.sortByCount("$tags");
        // 新建Facet对象,传入第一组分类的接收名称,以及在第一组分类中要做的聚合操作。
        Facet categorizedByTags = new Facet("categorizedByTags", unwindBsonForByTags, sortByCountBsonForByTags);

        // Facet中第二组分类(categorizedByPrice)的聚合操作match 和 match
        Bson matchBsonForPrice = Aggregates.match(Filters.exists("price"));
        // 这里面要新建BsonField构建 {"count": { $sum: 1 }  和 "titles": { $push: "$title" }} 作为第二组分类中$Bucket聚合操作中output值
        BsonField countOutput = new BsonField("count", new Document("$sum", 1));
        BsonField titleOutput = new BsonField("titles", new Document("$push", "$price"));
        // 上面2个操作传入到BucketOption对象,最后传到bucket操作
        BucketOptions bucketOptions = new BucketOptions().defaultBucket("Other").output(countOutput).output(titleOutput);
        Bson bucketBsonForByPrice = Aggregates.bucket("$price", Arrays.asList(0, 150, 200, 300, 400), bucketOptions);
        Facet categorizedByPrice = new Facet("categorizedByPrice", matchBsonForPrice, bucketBsonForByPrice);

        // Facet中第三组分类 (categorizedByYears(Auto))的聚合操作,按年自动分成4个区间。
        Bson bucketAutoBsonForByYears = Aggregates.bucketAuto("$year", 4);
        Facet categorizedByYears = new Facet("categorizedByYears", bucketAutoBsonForByYears);

        // 新建一个List<Facet>把每组分类的Facet对象传进去。
        List<Facet> facetList = new ArrayList<>();
        facetList.add(categorizedByTags);
        facetList.add(categorizedByPrice);
        facetList.add(categorizedByYears);
        // 调用Aggregates的facet方法,传入List<Facet>得到最终Bson对象,并添加到Bson集合中。
        Bson facetBson = Aggregates.facet(facetList);
        List<Bson> bsonList = new ArrayList<>();
        bsonList.add(facetBson);
        // 调用方法执行得到结果
        MongoCollection<Document> collection = mongoTemplate.getCollection(ARTWORK_COLLECTION);
        AggregateIterable<Document> resultList = collection.aggregate(bsonList);
        for (Document document : resultList) {
            System.out.println("result is :" + document);
        }
    }

$graphLookup 文档递归查询

格式

{
   $graphLookup: {
      from: <collection>,
      startWith: <expression>,
      connectFromField: <string>,
      connectToField: <string>,
      as: <string>,
      maxDepth: <number>,
      depthField: <string>,
      restrictSearchWithMatch: <document>
   }
}

注意事项
$graphLookup递归查询会用到内存占用

db.collection.aggregate([
   { $graphLookup: { from: "fromCollection", ... } }
])

The collection can be sharded.
The fromCollection cannot be sharded.

db.employees.insertMany([
  { "_id" : 1, "name" : "Dev" },
{ "_id" : 2, "name" : "Eliot", "reportsTo" : "Dev" },
{ "_id" : 3, "name" : "Ron", "reportsTo" : "Eliot" },
{ "_id" : 4, "name" : "Andrew", "reportsTo" : "Eliot" },
{ "_id" : 5, "name" : "Asya", "reportsTo" : "Ron" },
{ "_id" : 6, "name" : "Dan", "reportsTo" : "Andrew" }
  ])
db.employees.aggregate( [
   {
      $graphLookup: {
         from: "employees",
         startWith: "$reportsTo",
         connectFromField: "reportsTo",
         connectToField: "name",
         as: "reportingHierarchy"
      }
   }
] )
{ _id: 1, name: 'Dev', reportingHierarchy: [] }
{ _id: 2,
  name: 'Eliot',
  reportsTo: 'Dev',
  reportingHierarchy: [ { _id: 1, name: 'Dev' } ] }
{ _id: 3,
  name: 'Ron',
  reportsTo: 'Eliot',
  reportingHierarchy: 
   [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' },
     { _id: 1, name: 'Dev' } ] }
{ _id: 4,
  name: 'Andrew',
  reportsTo: 'Eliot',
  reportingHierarchy: 
   [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' },
     { _id: 1, name: 'Dev' } ] }
{ _id: 5,
  name: 'Asya',
  reportsTo: 'Ron',
  reportingHierarchy: 
   [ { _id: 3, name: 'Ron', reportsTo: 'Eliot' },
     { _id: 2, name: 'Eliot', reportsTo: 'Dev' },
     { _id: 1, name: 'Dev' } ] }
{ _id: 6,
  name: 'Dan',
  reportsTo: 'Andrew',
  reportingHierarchy: 
   [ { _id: 2, name: 'Eliot', reportsTo: 'Dev' },
     { _id: 4, name: 'Andrew', reportsTo: 'Eliot' },
     { _id: 1, name: 'Dev' } ] }

以下以Ron为例
在这里插入图片描述

跨多文档递归$graphLookup

db.airports.insertMany( [
   { "_id" : 0, "airport" : "JFK", "connects" : [ "BOS", "ORD" ] },
   { "_id" : 1, "airport" : "BOS", "connects" : [ "JFK", "PWM" ] },
   { "_id" : 2, "airport" : "ORD", "connects" : [ "JFK" ] },
   { "_id" : 3, "airport" : "PWM", "connects" : [ "BOS", "LHR" ] },
   { "_id" : 4, "airport" : "LHR", "connects" : [ "PWM" ] }
] )
db.travelers.insertMany( [
   { "_id" : 1, "name" : "Dev", "nearestAirport" : "JFK" },
   { "_id" : 2, "name" : "Eliot", "nearestAirport" : "JFK" },
   { "_id" : 3, "name" : "Jeff", "nearestAirport" : "BOS" }
] )
db.travelers.aggregate( [
   {
      $graphLookup: {
         from: "airports",
         startWith: "$nearestAirport",
         connectFromField: "connects",
         connectToField: "airport",
         maxDepth: 2,
         depthField: "numConnections",
         as: "destinations"
      }
   }
] )

结果为

{
   "_id" : 1,
   "name" : "Dev",
   "nearestAirport" : "JFK",
   "destinations" : [
      { "_id" : 3,
        "airport" : "PWM",
        "connects" : [ "BOS", "LHR" ],
        "numConnections" : NumberLong(2) },
      { "_id" : 2,
        "airport" : "ORD",
        "connects" : [ "JFK" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 1,
        "airport" : "BOS",
        "connects" : [ "JFK", "PWM" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 0,
        "airport" : "JFK",
        "connects" : [ "BOS", "ORD" ],
        "numConnections" : NumberLong(0) }
   ]
}
{
   "_id" : 2,
   "name" : "Eliot",
   "nearestAirport" : "JFK",
   "destinations" : [
      { "_id" : 3,
        "airport" : "PWM",
        "connects" : [ "BOS", "LHR" ],
        "numConnections" : NumberLong(2) },
      { "_id" : 2,
        "airport" : "ORD",
        "connects" : [ "JFK" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 1,
        "airport" : "BOS",
        "connects" : [ "JFK", "PWM" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 0,
        "airport" : "JFK",
        "connects" : [ "BOS", "ORD" ],
        "numConnections" : NumberLong(0) } ]
}
{
   "_id" : 3,
   "name" : "Jeff",
   "nearestAirport" : "BOS",
   "destinations" : [
      { "_id" : 2,
        "airport" : "ORD",
        "connects" : [ "JFK" ],
        "numConnections" : NumberLong(2) },
      { "_id" : 3,
        "airport" : "PWM",
        "connects" : [ "BOS", "LHR" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 4,
        "airport" : "LHR",
        "connects" : [ "PWM" ],
        "numConnections" : NumberLong(2) },
      { "_id" : 0,
        "airport" : "JFK",
        "connects" : [ "BOS", "ORD" ],
        "numConnections" : NumberLong(1) },
      { "_id" : 1,
        "airport" : "BOS",
        "connects" : [ "JFK", "PWM" ],
        "numConnections" : NumberLong(0) }
   ]
}

递归匹配时候,增加过滤

  {
  "_id" : 1,
  "name" : "Tanya Jordan",
  "friends" : [ "Shirley Soto", "Terry Hawkins", "Carole Hale" ],
  "hobbies" : [ "tennis", "unicycling", "golf" ]
},
{
  "_id" : 2,
  "name" : "Carole Hale",
  "friends" : [ "Joseph Dennis", "Tanya Jordan", "Terry Hawkins" ],
  "hobbies" : [ "archery", "golf", "woodworking" ]
},
{
  "_id" : 3,
  "name" : "Terry Hawkins",
  "friends" : [ "Tanya Jordan", "Carole Hale", "Angelo Ward" ],
  "hobbies" : [ "knitting", "frisbee" ]
},
{
  "_id" : 4,
  "name" : "Joseph Dennis",
  "friends" : [ "Angelo Ward", "Carole Hale" ],
  "hobbies" : [ "tennis", "golf", "topiary" ]
},
{
  "_id" : 5,
  "name" : "Angelo Ward",
  "friends" : [ "Terry Hawkins", "Shirley Soto", "Joseph Dennis" ],
  "hobbies" : [ "travel", "ceramics", "golf" ]
},
{
   "_id" : 6,
   "name" : "Shirley Soto",
   "friends" : [ "Angelo Ward", "Tanya Jordan", "Carole Hale" ],
   "hobbies" : [ "frisbee", "set theory" ]
 }
  ])

1.查询

db.people.aggregate([
  {$match:{name:"Tanya Jordan"}},
  {$graphLookup:{
    from:"people",
    startWith:"$friends",
    connectFromField:"friend",
    connectToField:"name",
    as:"golfers"
  
  }}
  ])
{ _id: 1,
  name: 'Tanya Jordan',
  friends: [ 'Shirley Soto', 'Terry Hawkins', 'Carole Hale' ],
  hobbies: [ 'tennis', 'unicycling', 'golf' ],
  golfers: 
   [ { _id: 2,
       name: 'Carole Hale',
       friends: [ 'Joseph Dennis', 'Tanya Jordan', 'Terry Hawkins' ],
       hobbies: [ 'archery', 'golf', 'woodworking' ] },
     { _id: 3,
       name: 'Terry Hawkins',
       friends: [ 'Tanya Jordan', 'Carole Hale', 'Angelo Ward' ],
       hobbies: [ 'knitting', 'frisbee' ] },
     { _id: 6,
       name: 'Shirley Soto',
       friends: [ 'Angelo Ward', 'Tanya Jordan', 'Carole Hale' ],
       hobbies: [ 'frisbee', 'set theory' ] } ] }

2.增加匹配限制

db.people.aggregate([
  {$match:{name:"Tanya Jordan"}},
  {$graphLookup:{
    from:"people",
    startWith:"$friends",
    connectFromField:"friends",
    connectToField:"name",
    as:"golfers",
    restrictSearchWithMatch: {"hobbies":"golf"},
    depthField:"length"
   
  
  }}
  ])
{ _id: 1,
  name: 'Tanya Jordan',
  friends: [ 'Shirley Soto', 'Terry Hawkins', 'Carole Hale' ],
  hobbies: [ 'tennis', 'unicycling', 'golf' ],
  golfers: 
   [ { _id: 4,
       name: 'Joseph Dennis',
       friends: [ 'Angelo Ward', 'Carole Hale' ],
       hobbies: [ 'tennis', 'golf', 'topiary' ],
       length: 1 },
     { _id: 1,
       name: 'Tanya Jordan',
       friends: [ 'Shirley Soto', 'Terry Hawkins', 'Carole Hale' ],
       hobbies: [ 'tennis', 'unicycling', 'golf' ],
       length: 1 },
     { _id: 5,
       name: 'Angelo Ward',
       friends: [ 'Terry Hawkins', 'Shirley Soto', 'Joseph Dennis' ],
       hobbies: [ 'travel', 'ceramics', 'golf' ],
       length: 2 },
     { _id: 2,
       name: 'Carole Hale',
       friends: [ 'Joseph Dennis', 'Tanya Jordan', 'Terry Hawkins' ],
       hobbies: [ 'archery', 'golf', 'woodworking' ],
       length: 0 } ] }

增加显示输出字段设置

db.people.aggregate([
  {$match:{name:"Tanya Jordan"}},
  {$graphLookup:{
    from:"people",
    startWith:"$friends",
    connectFromField:"friends",
    connectToField:"name",
    as:"golfers",
    restrictSearchWithMatch: {"hobbies":"golf"},
    depthField:"length"
  }},
  {
    $project:{
      name:1,
      friends:1,
      "connections who play golf":"$golfers.name"
    }
  }
  ])
{ _id: 1,
  name: 'Tanya Jordan',
  friends: [ 'Shirley Soto', 'Terry Hawkins', 'Carole Hale' ],
  'connections who play golf': [ 'Joseph Dennis', 'Tanya Jordan', 'Angelo Ward', 'Carole Hale' ] }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值