当前位置: 首页 > news >正文

MongoDB系列:管道操作:聚合阶段操作符(二)

MongoDB系列:管道操作:聚合阶段操作符(二)

聚合阶段操作符介绍

本节只编写了个人认为可能用到的操作符,详细更多的操作符以及使用注意事项请前往MongoDB官网。

$match

过滤匹配数据。

// 插入数据
db.orders.insertMany( [{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
] )// 查询 item = almonds的文档
db.orders.aggregate([{$match: {item:"almonds"}
}])
// 查询orice >=10 的数据
db.orders.aggregate([{$match: {price:{$gte:10}}
}])

$group

聚合分组,类似于sql种的分组用法。

{$group:{_id: <expression>, // Group key<field1>: { <accumulator1> : <expression1> }, // 返回的值,通常是聚合函数...}}

$limit

限制输出文档的个数。

// 获取books表中第一条数据
db.getCollection("books").aggregate([{$limit: 1
}])

$skip

跳过指定的个数的文档。

// 语法
{ $skip: <positive 64-bit integer> }

1、跳过第一条数据

// 插入10条数据
db.stastistic.insertMany([{ "_id": "2019Q1", "sales": 1950, "purchased": 1200 },{ "_id": "2019Q2", "sales": 500, "purchased": 1700 }{ "_id": "2019Q3", "sales": 1950, "purchased": 1200 },{ "_id": "2019Q4", "sales": 500, "purchased": 1700 },{ "_id": "2019Q5", "sales": 1950, "purchased": 1200 },{ "_id": "2019Q6", "sales": 500, "purchased": 1700 },{ "_id": "2019Q7", "sales": 1950, "purchased": 1200 },{ "_id": "2019Q8", "sales": 500, "purchased": 1700 },{ "_id": "2019Q9", "sales": 1950, "purchased": 1200 },{ "_id": "2019Q10", "sales": 500, "purchased": 1700 },
])// 跳过第一条数据
db.stastistic.aggregate([{$skip: 1}
])

2、配合上述$limit可以完成分页查询的效果

如按照每页5条,查询第二页的数据。

pageNumber:第几页,pageSize:数量,则

$skip 的 变量是 (pageNumber - 1) * pageSize

// 每页5条,查询第二页的树。
// (2 - 1) * 5 = 5
db.stastistic.aggregate([{$skip: 5},{$limit: 5}
])

$sort

排序。

// 说明:1:正序排列;-1:倒叙排列。也可以指定计算规则进行排序。
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }

1、按照id正序排列

db.stastistic.aggregate([{$sort: {_id:1}}
])

$count

统计文档的数量。

// 查询score>80分的数据的个数为passing_scores
db.scores.aggregate([{$match: {score: {$gt: 80}}},{$count: "passing_scores"}]
)

$unionWith

类似于sql中的union all的用法,将结果集进行合并。

// 定义
{ $unionWith: { coll: "<collection>", pipeline: [ <stage1>, ... ] } }{ $unionWith: "<collection>" }  // Include all documents from the specified collection

1、合并数据

// 插入数据
db.suppliers.insertMany([{ _id: 1, supplier: "Aardvark and Sons", state: "Texas" },{ _id: 2, supplier: "Bears Run Amok.", state: "Colorado"},{ _id: 3, supplier: "Squid Mark Inc. ", state: "Rhode Island" },
])db.warehouses.insertMany([{ _id: 1, warehouse: "A", region: "West", state: "California" },{ _id: 2, warehouse: "B", region: "Central", state: "Colorado"},{ _id: 3, warehouse: "C", region: "East", state: "Florida" },
])// 获取两个表中state
db.suppliers.aggregate([{$project: { state: 1, _id: 0 }},{$unionWith: {coll: "warehouses", pipeline: [{$project: { state: 1, _id: 0 }}]}}, {$group: { _id: "$state" }}
])
  • 第一阶段:只查询state属性
  • 第二阶段:合并warehousesstate属性
  • 第三阶段:利用分组过滤数据

2、统计多表中的数据进行分析

// 插入2017、2018、2019、2020销售数据清单
db.sales_2017.insertMany( [{ store: "General Store", item: "Chocolates", quantity: 150 },{ store: "ShopMart", item: "Chocolates", quantity: 50 },{ store: "General Store", item: "Cookies", quantity: 100 },{ store: "ShopMart", item: "Cookies", quantity: 120 },{ store: "General Store", item: "Pie", quantity: 10 },{ store: "ShopMart", item: "Pie", quantity: 5 }
] )db.sales_2018.insertMany( [{ store: "General Store", item: "Cheese", quantity: 30 },{ store: "ShopMart", item: "Cheese", quantity: 50 },{ store: "General Store", item: "Chocolates", quantity: 125 },{ store: "ShopMart", item: "Chocolates", quantity: 150 },{ store: "General Store", item: "Cookies", quantity: 200 },{ store: "ShopMart", item: "Cookies", quantity: 100 },{ store: "ShopMart", item: "Nuts", quantity: 100 },{ store: "General Store", item: "Pie", quantity: 30 },{ store: "ShopMart", item: "Pie", quantity: 25 }
] )db.sales_2019.insertMany( [{ store: "General Store", item: "Cheese", quantity: 50 },{ store: "ShopMart", item: "Cheese", quantity: 20 },{ store: "General Store", item: "Chocolates", quantity: 125 },{ store: "ShopMart", item: "Chocolates", quantity: 150 },{ store: "General Store", item: "Cookies", quantity: 200 },{ store: "ShopMart", item: "Cookies", quantity: 100 },{ store: "General Store", item: "Nuts", quantity: 80 },{ store: "ShopMart", item: "Nuts", quantity: 30 },{ store: "General Store", item: "Pie", quantity: 50 },{ store: "ShopMart", item: "Pie", quantity: 75 }
] )db.sales_2020.insertMany( [{ store: "General Store", item: "Cheese", quantity: 100, },{ store: "ShopMart", item: "Cheese", quantity: 100},{ store: "General Store", item: "Chocolates", quantity: 200 },{ store: "ShopMart", item: "Chocolates", quantity: 300 },{ store: "General Store", item: "Cookies", quantity: 500 },{ store: "ShopMart", item: "Cookies", quantity: 400 },{ store: "General Store", item: "Nuts", quantity: 100 },{ store: "ShopMart", item: "Nuts", quantity: 200 },{ store: "General Store", item: "Pie", quantity: 100 },{ store: "ShopMart", item: "Pie", quantity: 100 }
] )// 统计四年各个产品的销售数量
db.sales_2017.aggregate([{$unionWith: "sales_2018"},{$unionWith: "sales_2019"},{$unionWith: "sales_2020"},{$group: {_id: "$item",total: {$sum: "$quantity"}}}
])// 执行结果
{ "_id" : "Chocolates", "total" : 1250 }
{ "_id" : "Cookies", "total" : 1720 }
{ "_id" : "Pie", "total" : 395 }
{ "_id" : "Cheese", "total" : 350 }
{ "_id" : "Nuts", "total" : 510 }

$unset

删除字段,在管道中等同于$project庄涛为0的操作。

// 删除单个属性
{ $unset: "<field>" }
// 删除多个属性
{ $unset: [ "<field1>", "<field2>", ... ] }

插入数据:

db.books.insertMany([{ "_id" : 1, title: "Antelope Antics", isbn: "0001122223334", author: { last:"An", first: "Auntie" }, copies: [ { warehouse: "A", qty: 5 }, { warehouse: "B", qty: 15 } ] },{ "_id" : 2, title: "Bees Babble", isbn: "999999999333", author: { last:"Bumble", first: "Bee" }, copies: [ { warehouse: "A", qty: 2 }, { warehouse: "B", qty: 5 } ] }
])

1、删除单个及多个字段

// 删除title字段
db.books.aggregate([{$unset: "title"
}])
// 删除title和isbn字段
db.books.aggregate([{$unset: ["title", "isbn"]
}])

2、删除对象以及数组中对象

// 删除对象中first属性
db.books.aggregate([{$unset: "author.first"
}])// 删除数组中对象的属性warehouse
db.books.aggregate([{$unset: "copies.warehouse"
}])

$unwind

将数组元素进行平铺并提取到外层。

// 语法
{$unwind:{path: <field path>,			// 数组元素字段名includeArrayIndex: <string>,	// 指定平铺出来之后当前数据位于数组索引的名称preserveNullAndEmptyArrays: <boolean>	//当数组元素为null或空数组的处理}
}
  • preserveNullAndEmptyArrays:true 若为null或空数组的时候保留数据,false忽略数据。默认false

1、提取数组到外层

// 插入数据
db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })// 将数组元素提取出来平铺到外层
db.inventory.aggregate([{$unwind: {path: "$sizes"}
}])// 执行结果
{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }

2、元素为null或空数组的处理,以及输出在数组中的索引位置

// 插入元素
db.inventory2.insertMany([{ "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] },{ "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] },{ "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" },{ "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") },{ "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null }
])// 当为false时,忽略null和空数组的数据,平铺到外层时在数组的索引指定为arrayIndex
db.inventory2.aggregate([{$unwind: {path: "$sizes",includeArrayIndex: "arrayIndex",preserveNullAndEmptyArrays: false}
}])// 执行结果
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "S", "arrayIndex" : { "$numberLong" : "0" } }
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "M", "arrayIndex" : { "$numberLong" : "1" } }
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "L", "arrayIndex" : { "$numberLong" : "2" } }
{ "_id" : 3, "item" : "IJK", "price" : { "$numberDecimal" : "160" }, "sizes" : "M", "arrayIndex" : null }// 修改preserveNullAndEmptyArrays为true,展示出列所有的数据
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "S", "arrayIndex" : { "$numberLong" : "0" } }
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "M", "arrayIndex" : { "$numberLong" : "1" } }
{ "_id" : 1, "item" : "ABC", "price" : { "$numberDecimal" : "80" }, "sizes" : "L", "arrayIndex" : { "$numberLong" : "2" } }
{ "_id" : 2, "item" : "EFG", "price" : { "$numberDecimal" : "120" }, "arrayIndex" : null }
{ "_id" : 3, "item" : "IJK", "price" : { "$numberDecimal" : "160" }, "sizes" : "M", "arrayIndex" : null }
{ "_id" : 4, "item" : "LMN", "price" : { "$numberDecimal" : "10" }, "arrayIndex" : null }
{ "_id" : 5, "item" : "XYZ", "price" : { "$numberDecimal" : "5.75" }, "sizes" : null, "arrayIndex" : null }

3、平铺数组中是对象的且对象中还是数组

插入数据

db.sales.insertMany([{_id: "1","items" : [{"name" : "pens","tags" : [ "writing", "office", "school", "stationary" ],"price" : NumberDecimal("12.00"),"quantity" : NumberInt("5")},{"name" : "envelopes","tags" : [ "stationary", "office" ],"price" : NumberDecimal("19.95"),"quantity" : NumberInt("8")}]},{_id: "2","items" : [{"name" : "laptop","tags" : [ "office", "electronics" ],"price" : NumberDecimal("800.00"),"quantity" : NumberInt("1")},{"name" : "notepad","tags" : [ "stationary", "school" ],"price" : NumberDecimal("14.95"),"quantity" : NumberInt("3")}]}
])

执行命令:

db.sales.aggregate([// First Stage{ $unwind: "$items" },// Second Stage{ $unwind: "$items.tags" },// Third Stage{$group:{_id: "$items.tags",totalSalesAmount:{$sum: { $multiply: [ "$items.price", "$items.quantity" ] }}}}
])db.sales.aggregate([{$unwind: {path: "$items"}
}, {$unwind: {path: "$items.tags"}
}, {$group: {_id: "$items.tags",sumSale: { $sum: { $multiply: ["$items.price", "$items.quantity"] } }}
}, {$sort: {sumSale: 1}
}])// 输出结果
{"_id" : "writing","sumSale" : 60.0
}
{"_id" : "school","sumSale" : 104.85
}
{"_id" : "stationary","sumSale" : 264.45
}
{"_id" : "electronics","sumSale" : 800.0
}
{"_id" : "office","sumSale" : 1019.6
}
  • 第一阶段:将items的数组进行平铺提取,这时tags成了items中的一个属性。
  • 第二阶段:将items中的tags再次平铺提取。
  • 第三阶段:按照tages进行分组,计算销售额,price * quantity再求和。

$addFields / $set

添加字段,可以添加一个普通的字段以及添加嵌入式字段如对象中添加新的属性。

创建并插入数据到文档中:

db.vehicles.insertMany([{ _id: 1, type: "car", specs: { doors: 4, wheels: 4 }, times: [2, 3, 4, 5] },{ _id: 2, type: "motorcycle", specs: { doors: 0, wheels: 2 }, times: [77, 89, 21] },{ _id: 3, type: "jet ski" }]
)

1、直接插入字段值到文档中

// 插入了sumTimes是根据times的和,address是American。
db.vehicles.aggregate([{$addFields: {sumTimes: { $sum: "$times" },address: "American"}}
])// 结果
{ "_id" : 1, "type" : "car", "specs" : { "doors" : 4, "wheels" : 4 }, "times" : [ 2, 3, 4, 5 ], "sumTimes" : 14, "address" : "American" }
{ "_id" : 2, "type" : "motorcycle", "specs" : { "doors" : 0, "wheels" : 2 }, "times" : [ 77, 89, 21 ], "sumTimes" : 187, "address" : "American" }
{ "_id" : 3, "type" : "jet ski", "sumTimes" : 0, "address" : "American" }

2、插入文档到对象类型中

// 在specs对象中添加ball属性,值设置为 小球
// 下面有两种写发,第一种就是点来进行对象下穿,第二种就是利用json分层,最终效果是一样的
db.vehicles.aggregate([{$addFields: {//"specs.ball": "小球",specs: { ball: "小球" }}
}])// 结果
{ "_id" : 1, "type" : "car", "specs" : { "doors" : 4, "wheels" : 4, "ball" : "小球" }, "times" : [ 2, 3, 4, 5 ] }
{ "_id" : 2, "type" : "motorcycle", "specs" : { "doors" : 0, "wheels" : 2, "ball" : "小球" }, "times" : [ 77, 89, 21 ] }
{ "_id" : 3, "type" : "jet ski", "specs" : { "ball" : "小球" } }

3、插入文档到数组类型中

利用$concatArrays操作符,指定一个数组表达式,然后拼接数组类型的数据

  • $concatArrays 合并数组,按照输入文档的顺序合并,后续会详细介绍。
// 将id等于1的数据,times数组中将 1 插入到 times的前面。
db.vehicles.aggregate([{ $match: { _id: 1 } },{$addFields: {times: { $concatArrays: [[1], "$times"] }}}])// 结果,原本是 2,3,4,5
{ "_id" : 1, "type" : "car", "specs" : { "doors" : 4, "wheels" : 4 }, "times" : [ 1, 2, 3, 4, 5 ] }

$bucket

$bucket 是 MongoDB 聚合管道中的一个阶段,它用于将文档按照指定的范围进行分组成桶(buckets)。每个桶都包含一个特定范围的文档数量。

创建并插入数据

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" }
])

示例:

$bucket 有一下几个参数配置:

  • groupBy:分组字段
  • boundaries:桶的边界项,数组
  • default:没有分配给桶的项,_id使用默认的值
  • output:输出项。
// 按照year_born分组,分成[1840,1850),[1850,1860),[1860,1870),[1870,1880)的组称为桶。
// 输出每个桶的数量为count,并将桶中的name,year_born组成对象用$push放入到数组中。
// 最后获取count>3的数据db.artists.aggregate( [// First Stage{$bucket: {groupBy: "$year_born",                        // Field to group byboundaries: [ 1840, 1850, 1860, 1870, 1880 ], // Boundaries for the bucketsdefault: "Other",                             // Bucket ID for documents which do not fall into a bucketoutput: {                                     // 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} }}
] )// 输出结果:
{ "_id" : 1860.0, "count" : 4.0, "artists" : [ { "name" : "Emil Bernard", "year_born" : 1868 }, { "name" : "Joszef Rippl-Ronai", "year_born" : 1861 }, { "name" : "Alfred Maurer", "year_born" : 1868 }, { "name" : "Edvard Munch", "year_born" : 1863 } ] }

$fill

填充为null的值或者缺失的字段值。

{$fill: {partitionBy: <expression>,		//分组表达式,如按照部门分组partitionBy:$orgpartitionByFields: [ <field 1>, <field 2>, ... , <field n> ],sortBy: {<sort field 1>: <sort order>,<sort field 2>: <sort order>,...,<sort field n>: <sort order>	//排序表达式:create_time:1正序排列},output: {<field 1>: { value: <expression> },<field 2>: { method: <string> },	// 当前阶段输出,填充score:99或score:{method:linear or locf}...}}
}
  • linear:线性填充
  • locf:获取排序分组后同一组的前一个值
// 按照时间正序并且按照restaurant属性分组之后,填充score为每一组当前填充排序的前一个值
db.restaurantReviewsMultiple.aggregate( [{$fill:{sortBy: { date: 1 },partitionBy:  "$restaurant",output:{"score": { method: "locf" }}}}
] )

创建并插入数

db.sales.insertMany([{ "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") },{ "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") },{ "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") },{ "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" :  Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") },{ "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") },{ "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") },{ "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") },{ "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") },
])

1、查询文档种的数量

db.sales.aggregate( [{$group: {_id: null,count: { $count: { } }}}
] )// 结果
{ "_id" : null, "count" : 8 }// 等同于
// select count(1) from sales

2、分组之后过滤

// 查询每种类型的售出额,按照item分组,然后求售出额
// 最后获取售出额>=100的文档
db.sales.aggregate([// First Stage{$group :{_id : "$item",totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }}},// Second Stage{$match: { "totalSaleAmount": { $gte: 100 } }}])

sql表达式:

select item,sum(price * quantity) as totalSaleAmount from sales group by item having totalSaleAmount>=100

3、分组之后添加合并分类

// 插入数据
db.books.insertMany([{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])// 按照author分组,把每个组的数据插入到booksList数组中,最后统计每组复制的次数。
db.getCollection("books").aggregate([{$group: {_id: "$author",booksList: { $push: "$$ROOT" }}
}, {$addFields: {copyCount: { $sum: "$booksList.copies" }}
}])// 结果,以返回的一条数据为例
{"_id": "Dante","booksList": [{"_id": 8751,"title": "The Banquet","author": "Dante","copies": 2},{"_id": 8752,"title": "Divine Comedy","author": "Dante","copies": 1},{"_id": 8645,"title": "Eclogues","author": "Dante","copies": 2}],"copyCount": 5
}

$lookup

MongoDB的左外连接处理数据,也可用于子查询。

{$lookup:{from: <foreign collection>,	// 关联的表localField: <field from local collection's documents>,	// 当前表的关联字段foreignField: <field from foreign collection's documents>,	// 关联表的关联字段let: { <var_1>: <expression>,, <var_n>: <expression> }, // 当前表的字段取别名,在pipeline使用pipeline: [ <pipeline to run> ],		// 当前表与关联表的关联关系,可多字段且配合表达式使用as: <output array field>	// 输出字段名}
}

1、通过单字段关联查询

// 插入数据
db.orders.insertMany( [{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },{ "_id" : 3  }
] )db.inventory.insertMany( [{ "_id" : 1, "sku" : "almonds", "description": "product 1", "instock" : 120 },{ "_id" : 2, "sku" : "bread", "description": "product 2", "instock" : 80 },{ "_id" : 3, "sku" : "cashews", "description": "product 3", "instock" : 60 },{ "_id" : 4, "sku" : "pecans", "description": "product 4", "instock" : 70 },{ "_id" : 5, "sku": null, "description": "Incomplete" },{ "_id" : 6 }
] )// orders表左外关联inventory,然后根据item与sku的关联关系,对inventory的数据赋值到skuTt数组中
db.orders.aggregate([{$lookup: {from: "inventory",localField: "item",foreignField: "sku",as: "skuTt"}
}])// 结果
{"_id" : 1,"item" : "almonds","price" : 12,"quantity" : 2,"inventory_docs" : [{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }]
}
{"_id" : 2,"item" : "pecans","price" : 20,"quantity" : 1,"inventory_docs" : [{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }]
}
{"_id" : 3,"inventory_docs" : [{ "_id" : 5, "sku" : null, "description" : "Incomplete" },{ "_id" : 6 }]
}

其实和sql中的左外连接类似,以左表为基准,匹配右表中的数据,最后返回,但是MongoDB是将右表的数据输出到指定的字段数组上,而sql是平铺出来。值得注意的是,如果orders表中item字段是数组,该写法也同样适配。

sql写法为:

select o.*,i.* from orders o left join inventory i on o.item = i.sku

2、通过单字段关联,利用$mergeObjects将数据平铺

意思是返回和sql关联查询一对多场景中一样的结果,如A关联B,A有2条数据,B有4条数据,且相关联,则最终结果返回4条数据。

示例:

// 插入文档
db.orders.insertMany( [{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
] )
db.items.insertMany( [{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }{ "_id" : 4, "item" : "almonds", description: "almond clusters copy", "instock" : 240 }
] )//
db.orders.aggregate([// 第一阶段,形成了类似于 示例 1 中的格式{$lookup: {from: "items",	// 右表localField: "item",    // 左表关联字段foreignField: "item",  // 右表关联字段as: "fromItems" // 输出字段}},// 第二阶段// $unwind 这儿简要说明一下,意思是将数组进行平铺出来,每个数组中的值都成为了一条数据{$unwind: "$fromItems"},// 第三阶段// $mergeObjects 合并第二阶段的结果与左表的文档,若字段key一致则使用左表的值。将多个文档合并成单个文档。// $replaceRoot 替换回原文档中{$replaceRoot: { newRoot: { $mergeObjects: ["$fromItems", "$$ROOT"] } }},// 第四阶段,隐藏fromItems字段{$project: { "fromItems": 0 }}
])

解析:

  • 第一阶段与上述 示例一 一个意思,把右表的数据根据关联关系生成formItems的数组,成为左表orders表的一个属性。

  • 第二阶段将formItems数组平铺到orders表的上,formItems不再是一个数组而是一个对象,orders文档数据根据数组内的个数进行增加

    { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2, "fromItems" : { "_id" : 1, "item" : "almonds", "description" : "almond clusters", "instock" : 120 } }
    { "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2, "fromItems" : { "_id" : 4, "item" : "almonds", "description" : "都大大大大大", "instock" : 111 } }
    { "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1, "fromItems" : { "_id" : 3, "item" : "pecans", "description" : "candied pecans", "instock" : 60 } }
    
  • 第三阶段提取formItems内的数据到外面。

  • 第四阶段隐藏formItems属性,最终结果如下

    { "_id" : 1, "item" : "almonds", "description" : "almond clusters", "instock" : 120, "price" : 12, "quantity" : 2 }
    { "_id" : 1, "item" : "almonds", "description" : "都大大大大大", "instock" : 111, "price" : 12, "quantity" : 2 }
    { "_id" : 2, "item" : "pecans", "description" : "candied pecans", "instock" : 60, "price" : 20, "quantity" : 1 }
    

3、多个条件的关联查询

//插入数据
db.orders.insertMany( [{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
] )db.warehouses.insertMany( [{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
] )// orders与warehouses根据item与stock_item和warehouses.instock>orders.order_qty的文档关联。
// 不获取stock_item和_id
// pipeline内的写法是固定写法,太**了
db.orders.aggregate([{$lookup:{from: "warehouses",let: { order_item: "$item", order_qty: "$ordered" },pipeline: [{$match:{$expr:{$and:[{ $eq: ["$stock_item", "$$order_item"] },{ $gte: ["$instock", "$$order_qty"] }]}}},{ $project: { stock_item: 0, _id: 0 } }],as: "stockdata"}}
])// 结果
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2, "stockdata" : [ { "warehouse" : "A", "instock" : 120 }, { "warehouse" : "B", "instock" : 60 } ] }
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1, "stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60, "stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }

还可以利用localFieldforeignField来进行关联itemstock_item

$merge

合并数据,并输出到指定的表中,该阶段只能位于聚合得最后一个阶段中。

{ $merge: {into: <collection> -or- { db: <db>, coll: <collection> },on: <identifier field> -or- [ <identifier field1>, ...],  // Optionallet: <variables>,                                         // OptionalwhenMatched: <replace|keepExisting|merge|fail|pipeline>,  // OptionalwhenNotMatched: <insert|discard|fail>                     // Optional
} }
  • into:必选;指定集合名称,有两种用法。一直接写表名,表示再当前数据库下输出;二指定数据库输出。
    • into:xxx
    • {db:xxx,coll:xxx}
  • on:可选;指定输出时得唯一得字段,默认是_id。换言之也就是输出到哪条数据上。
  • let:可选:指定变量,应用在下面两个属性中。
  • whenMatched:可选;管道输出结果与原文档匹配时:replace、keepExisting、merge、fail,也可用表达式。
    • replace:替换,管道数据直接覆盖原数据。
    • keepExisting:使用原文档数据。
    • merge:管道数据与原数据合并,若字段一致得使用管道数据。默认的
    • fail:直接失败。
  • whenNotMatched:可选;管道输出结果与原文档无匹配时:insert、discard、fail
    • insert:直接插入,默认的。
    • discard:丢弃。
    • fail:直接失败。

1、统计数据合并到表中

// 插入数据
db.salaries.insertMany([{ "_id": 1, employee: "Ant", dept: "A", salary: 100000, fiscal_year: 2017 },{ "_id": 2, employee: "Bee", dept: "A", salary: 120000, fiscal_year: 2017 },{ "_id": 3, employee: "Cat", dept: "Z", salary: 115000, fiscal_year: 2017 },{ "_id": 4, employee: "Ant", dept: "A", salary: 115000, fiscal_year: 2018 },{ "_id": 5, employee: "Bee", dept: "Z", salary: 145000, fiscal_year: 2018 },{ "_id": 6, employee: "Cat", dept: "Z", salary: 135000, fiscal_year: 2018 },{ "_id": 7, employee: "Gecko", dept: "A", salary: 100000, fiscal_year: 2018 },{ "_id": 8, employee: "Ant", dept: "A", salary: 125000, fiscal_year: 2019 },{ "_id": 9, employee: "Bee", dept: "Z", salary: 160000, fiscal_year: 2019 },{ "_id": 10, employee: "Cat", dept: "Z", salary: 150000, fiscal_year: 2019 },{ "_id": 11, "employee": "Wren", "dept": "Z", "salary": 100000, "fiscal_year": 2019 },{ "_id": 12, "employee": "Zebra", "dept": "A", "salary": 150000, "fiscal_year": 2019 },{ "_id": 13, "employee": "headcount1", "dept": "Z", "salary": 120000, "fiscal_year": 2020 },{ "_id": 14, "employee": "headcount2", "dept": "Z", "salary": 120000, "fiscal_year": 2020 }
])// 查询统计某年某部门发放薪资得情况
db.getCollection("salaries").aggregate([{$group: {_id: { fiscal_yeal: "$fiscal_year", dept: "$dept" },employee: { $push: "$employee" },sumSalary: { $sum: "$salary" }}}, {$merge: {into: "my_statistics",on: "_id",whenMatched: "merge",whenNotMatched: "insert"}}
])// merge中into也可用{db:"my_database",coll:"my_statistics"}表示生成到my_database库中得my_statistics表中。
  • 第一阶段:按照fiscal_yealdept分组,将部门和年份绑定成一组,并求和salary和将员工放入数组
  • 第二阶段:将第一阶段的_id作为唯一值区分,whenMatched定义了当聚合管道数据与表my_statistics中的 _id相等的时候进行合并策略,whenNotMatched根据_id判断缺失时使用插入策略。

2、将多个表统计合并为一个表

统计季度销售额与支出额。

// 插入数据
db.purchaseorders.insertMany( [{ _id: 1, quarter: "2019Q1", region: "A", qty: 200, reportDate: new Date("2019-04-01") },{ _id: 2, quarter: "2019Q1", region: "B", qty: 300, reportDate: new Date("2019-04-01") },{ _id: 3, quarter: "2019Q1", region: "C", qty: 700, reportDate: new Date("2019-04-01") },{ _id: 4, quarter: "2019Q2", region: "B", qty: 300, reportDate: new Date("2019-07-01") },{ _id: 5, quarter: "2019Q2", region: "C", qty: 1000, reportDate: new Date("2019-07-01") },{ _id: 6, quarter: "2019Q2", region: "A", qty: 400, reportDate: new Date("2019-07-01") },
] )db.reportedsales.insertMany( [{ _id: 1, quarter: "2019Q1", region: "A", qty: 400, reportDate: new Date("2019-04-02") },{ _id: 2, quarter: "2019Q1", region: "B", qty: 550, reportDate: new Date("2019-04-02") },{ _id: 3, quarter: "2019Q1", region: "C", qty: 1000, reportDate: new Date("2019-04-05") },{ _id: 4, quarter: "2019Q2", region: "B", qty: 500, reportDate: new Date("2019-07-02") },
] )// 统计支出额
db.purchaseorders.aggregate([{$group: {_id: "$quarter",purchased: { $sum: "$qty" }}},{$merge: {into: "my_account",on: "_id",whenMatched: "merge",whenNotMatched: "insert"}}
])
// 计算销售额
db.reportedsales.aggregate([{$group: {_id: "$quarter",sales: { $sum: "$qty" }}},{$merge: {into: "my_account",on: "_id",whenMatched: "merge",whenNotMatched: "insert"}}
])
  • 第一个聚合管道,统计支出额purchased,并统计到my_account表中,结构如下:
{ "_id" : "2019Q1", "purchased" : 1200 }
{ "_id" : "2019Q2", "purchased" : 1700 }
  • 第二个聚合管道,统计销售额slaes,并用合并策略根据id作为唯一键与第一个聚合管道的数据进行合并。
{ "_id" : "2019Q1", "purchased" : 1200, "sales" : 1950 }
{ "_id" : "2019Q2", "purchased" : 1700, "sales" : 500 }

3、自定义匹配策略

  • $addFields$set
  • $project$unset
  • $replaceRoot$replaceWith
// 插入数据
db.votes.insertMany( [{ date: new Date("2019-05-01"), "thumbsup" : 1, "thumbsdown" : 1 },{ date: new Date("2019-05-02"), "thumbsup" : 3, "thumbsdown" : 1 },{ date: new Date("2019-05-03"), "thumbsup" : 1, "thumbsdown" : 1 },{ date: new Date("2019-05-04"), "thumbsup" : 2, "thumbsdown" : 2 },{ date: new Date("2019-05-05"), "thumbsup" : 6, "thumbsdown" : 10 },{ date: new Date("2019-05-06"), "thumbsup" : 13, "thumbsdown" : 16 }
] )
// 先默认生成一个五月的统计数据,5.1-5.6的统计数据
db.monthlytotals.insertOne({ "_id" : "2019-05", "thumbsup" : 26, "thumbsdown" : 31 }
)
// 插入一条5.7号的数据
db.votes.insertOne({ date: new Date("2019-05-07"), "thumbsup" : 14, "thumbsdown" : 10 }
)// 将5.7日的数据也计算到月度统计表中。
db.votes.aggregate([{$match:{date: {$gte: new Date("2019-05-07")}}},{$project: { "_id": { $dateToString: { format: "%Y-%m", date: "$date" } }, thumbsup: 1, thumbsdown: 1 }},{$merge: {into: "monthlytotals",on: "_id",whenMatched: [{$addFields: {thumbsup: { $add: ["$thumbsup", "$$new.thumbsup"] },thumbsdown: { $add: ["$thumbsdown", "$$new.thumbsdown"] }}}]}}
])
  • 第一阶段:过滤时间大于5.7号的数据
  • 第二阶段:将年月日格式日期 转换成 年月输出
  • 第三阶段:管道数据thumbsupthumbsdownmonthlytotals表匹配的数据相加。
    • $thumbsupmonthlytotals表中的数据。
    • $$new.thumbsup是管道前一阶段的数据。
//输出结果:统计5.1-5.7的数据
{ "_id" : "2019-05", "thumbsup" : 40.0, "thumbsdown" : 41.0 }

$out

算是上述$merge的简化版本,只能根据_id替换对应的数据。

{ $out: { db: "<output-db>", coll: "<output-collection>" } }
或者
{ $out: ""}

1、将结果输出到文档中

// 插入数据
db.getSiblingDB("test").books.insertMany([{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])db.books.aggregate([{$group: {_id: "$author",books: { $push: "$title" }}},{$out:"authors"}
]){ "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] }
  • 第一阶段:按照author分组,将title放入到books数组中。
  • 第二阶段:输出结果到authors表中。

$replaceRoot / $replaceWith

替换数据,也可以理解为上述$merge的替换的简化版本。另有一些详细的注意地方请关注官网。

// 语法
{ $replaceRoot: { newRoot: <replacementDocument> } }{ $replaceWith: <replacementDocument> }

1、提取对象数据

// 插入数据
db.people.insertMany([{ "_id": 1, "name": "Arlene", "age": 34, "pets": { "dogs": 2, "cats": 1 } },{ "_id": 2, "name": "Sam", "age": 41, "pets": { "cats": 1, "fish": 3 } },{ "_id": 3, "name": "Maria", "age": 25 }
])
// 提取每个人宠物的个数 
// $replaceRoot用法
db.people.aggregate([{$replaceRoot: { newRoot:{ $mergeObjects: [{ name:"$name","dogs": 0, "cats": 0, "fish": 0, "birds": 0 }, "$pets"] }}}
])
// $replaceWith用法
db.people.aggregate([{$replaceWith: { $mergeObjects: [{ name:"$name","dogs": 0, "cats": 0, "fish": 0, "birds": 0 }, "$pets"] }}  
])// 输出结果
{ "name" : "Arlene", "dogs" : 2, "cats" : 1, "fish" : 0.0, "birds" : 0.0 }
{ "name" : "Sam", "dogs" : 0.0, "cats" : 1, "fish" : 3, "birds" : 0.0 }
{ "name" : "Maria", "dogs" : 0.0, "cats" : 0.0, "fish" : 0.0, "birds" : 0.0 }

2、提取数组对象数据

db.students.insertMany([{"_id" : 1,"grades" : [{ "test": 1, "grade" : 80, "mean" : 75, "std" : 6 },{ "test": 2, "grade" : 85, "mean" : 90, "std" : 4 },{ "test": 3, "grade" : 95, "mean" : 85, "std" : 6 }]},{"_id" : 2,"grades" : [{ "test": 1, "grade" : 90, "mean" : 75, "std" : 6 },{ "test": 2, "grade" : 87, "mean" : 90, "std" : 3 },{ "test": 3, "grade" : 91, "mean" : 85, "std" : 4 }]}
])// 获取分数大于等于90的学生数据
db.students.aggregate([{$unwind: "$grades"},{$match: {"grades.grade": { $gte: 90 }}},{ $replaceRoot: { newRoot: "$grades" } }
])// 执行结果
{ "test" : 3, "grade" : 95, "mean" : 85, "std" : 6 }
{ "test" : 1, "grade" : 90, "mean" : 75, "std" : 6 }
{ "test" : 3, "grade" : 91, "mean" : 85, "std" : 4 }
  • 第一阶段:提取grades数组元素。

    { "_id" : 1, "grades" : { "test" : 1, "grade" : 80, "mean" : 75, "std" : 6 } }
    { "_id" : 1, "grades" : { "test" : 2, "grade" : 85, "mean" : 90, "std" : 4 } }
    { "_id" : 1, "grades" : { "test" : 3, "grade" : 95, "mean" : 85, "std" : 6 } }
    { "_id" : 2, "grades" : { "test" : 1, "grade" : 90, "mean" : 75, "std" : 6 } }
    { "_id" : 2, "grades" : { "test" : 2, "grade" : 87, "mean" : 90, "std" : 3 } }
    { "_id" : 2, "grades" : { "test" : 3, "grade" : 91, "mean" : 85, "std" : 4 } }
    
  • 第二阶段:过滤grade>=90的数据

  • 第三阶段:将grades提取到最外层

相关文章:

MongoDB系列:管道操作:聚合阶段操作符(二)

MongoDB系列&#xff1a;管道操作&#xff1a;聚合阶段操作符&#xff08;二&#xff09; 聚合阶段操作符介绍 本节只编写了个人认为可能用到的操作符&#xff0c;详细更多的操作符以及使用注意事项请前往MongoDB官网。 $match 过滤匹配数据。 // 插入数据 db.orders.inse…...

C++ //练习 5.12 修改统计元音字母的程序,使其能统计以下含有两个字符的字符序列的数量:ff、fl和fi。

C Primer&#xff08;第5版&#xff09; 练习 5.12 练习 5.12 修改统计元音字母的程序&#xff0c;使其能统计以下含有两个字符的字符序列的数量&#xff1a;ff、fl和fi。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /****…...

C语言-----自定义类型-----结构体枚举联合

结构体和数组一样&#xff0c;都是一群数据的集合&#xff0c;不同的是数组当中的数据是相同的类型&#xff0c;但是结构体中的数据类型可以不相同&#xff0c;结构体里的成员叫做成员变量 结构体类型是C语言里面的一种自定义类型&#xff0c;我们前面已经了解到过int,char,fl…...

elasticsearch下载及可视化工具下载使用

elasticsearch下载及配置、启动 一、下载 Download Elasticsearch | Elastic 二、启动 双击bat即可。 出现如下说明启动成功&#xff1a; 访问测试&#xff1a; 三、注意 &#xff08;1&#xff09;因为es启动默认端口是&#xff1a;9200,所以需要检查此端口是否被占用。…...

vim常用命令以及配置文件

layout: article title: “vim文本编译器” vim文本编辑器 有三种模式: 命令模式 文本模式, 末行模式 vim命令大全 - 知乎 (zhihu.com) 命令模式 插入 i: 切换到输入模式&#xff0c;在光标当前位置开始输入文本。 a: 进入插入模式&#xff0c;在光标下一个位置开始输入文…...

2024年的VUE2下的无效指令npm install --save vue-i18n

vue官网已经声明了不再维护vue2, vue-i18n安装依赖的时候就只接安装vue3的vue-i18, 直接报错&#xff1a; > npm install --save vue-i18n npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! While resolving: yudao-ui-admin…...

计算机视觉主要知识点

计算机视觉是指利用计算机和算法来解析和理解图片和视频中的内容。这是一个跨学科领域&#xff0c;融合了计算机科学、图像处理、机器学习和模式识别等多方面的技术。以下是一些计算机视觉入门的基本知识点&#xff1a; 图像基础&#xff1a; 像素&#xff1a;图片的最基本组成…...

python 基础知识点(蓝桥杯python科目个人复习计划35)

今日复习计划&#xff1a;阶段总结&#xff08;新年贺礼&#xff09; 1.python简介&#xff08;定义&#xff0c;优点&#xff0c;缺点&#xff0c;应用领域&#xff09; python&#xff1a;一种广泛使用的解释型&#xff0c;高级和通用的编程语言 python极简&#xff0c;生…...

使用HTML、CSS和JavaScript来创建一个粒子效果,粒子会跟随鼠标点击位置生成

文章目录 一、粒子效果二、JavaScript三、HTML四、CSS五、热门文章 一、粒子效果 以下是一个简单的示例&#xff0c;使用HTML、CSS和JavaScript来创建一个粒子效果&#xff0c;粒子会跟随鼠标点击位置生成&#xff1a; <!DOCTYPE html> <html> <head><t…...

优质项目追踪平台一览:助力项目管理与监控

项目追踪平台是现代项目管理中不可或缺的工具&#xff0c;它可以帮助团队高效地跟踪和管理项目进度、任务和资源分配。在当今快节奏的商业环境中&#xff0c;有许多热门的项目追踪平台可供选择。 本文总结了当下热门的项目追踪平台&#xff0c;供您参考~ 1、Zoho Projects&…...

Docker下安装GitLab

极狐GitLab Docker 镜像 | 极狐GitLab 安装所需最小配置 内存至少4G 系统内核至少3.10以上 uname -r 命令可以查看系统内核版本 安装Docker 1.更新 yum源 yum update 2.安装依赖(如果在操作第三步的时候提示yum-config-manager 未找到命令 就安装下面依赖) yum instal…...

2024/2最新升级ChatGPT Plus的方法

2024年2月最新升级方法&#xff1a;ChatGPT Plus全方位体验指南 随着2024年2月的到来&#xff0c;ChatGPT Plus迎来了全新的升级。对于追求更高效率和更强大功能的用户来说&#xff0c;升级至ChatGPT Plus无疑是提升工作和学习体验的关键。在本指南中&#xff0c;我将一步一步…...

github和gitee

github GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持Git作为唯一的版本库格式进行托管&#xff0c;故名GitHub。 github可以给提交的代码打上标签&#xff0c;方便版本的迭代和回退&#xff0c;也是一个存储代码的仓库 github工作区 gitee是gitHub的…...

3秒实现无痛基于Stable Diffusion WebUI安装ComfyUI!无需重复安装环境!无需重复下载模型!安装教程

标题略有夸张的表达了接下来这一套确实很简单&#xff0c;相较于直接下载或者通过秋叶包更新而言。大大节省磁盘空间&#xff0c;和下载时间。 这篇教程不需要你有&#xff1a; 代码基础。都是复制粘贴就完事。魔法。 这篇教程默认你已经有&#xff1a; 1. 本地能够正常使用…...

【UE】游戏运行流程的简单理解

流程图 官方的游戏流程图&#xff1a; 一般顺序为初始化引擎、创建并初始化 GameInstance、加载关卡&#xff0c;最后开始游戏。 总的来说就是&#xff1a; 开始游戏-》游戏实例-》关卡-》游戏模式-》玩家控制器-》Pawn、玩家状态、HUD、UMG&#xff08;可有可无&#xff09; …...

【数据分析】Excel中的常用函数公式总结

目录 0 引用方式0.1 相对引用0.2 绝对引用0.3 混合引用0.4 3D引用0.5 命名引用 1 基础函数1.1 加法、减法、乘法和除法1.2 平均数1.3 求和1.4 最大值和最小值 2 文本函数2.1 合并单元格内容2.2 查找2.3 替换 3 逻辑函数3.1 IF函数3.2 AND和OR函数3.3 IFERROR函数 4 统计函数4.1…...

ESLint prettier 配置代码风格

环境同步&#xff1a; 1、ESlint&#xff0c;开启保存自动修复 配置文件 .eslintrc.cjs prettier 风格配置 https://prettier.io 使用单引号 不使用分号 每行宽度至多80字符 不加对象|数组最后逗号 换行符号不限制&#xff08;win mac 不一致&#xff09; vue组件名称…...

涤生大数据实战:基于Flink+ODPS历史累计计算项目分析与优化(上)

涤生大数据实战&#xff1a;基于FlinkODPS历史累计计算项目分析与优化&#xff08;一&#xff09; 1.前置知识 ODPS&#xff08;Open Data Platform and Service&#xff09;是阿里云自研的一体化大数据计算平台和数据仓库产品&#xff0c;在集团内部离线作为离线数据处理和存…...

jvm一级缓存

1、利用JVM缓存。脱离redis。 2、导包&#xff0c;springboot自带此包。如没有可以导&#xff1a;com.google.guava:guava:20.0的包。 3、直接上代码&#xff1a; package com.leo.cache;import com.alibaba.fastjson.JSONObject; import com.google.common.cache.Cache; im…...

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Web组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Web组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Web组件 提供具有网页显示能力的Web组件&#xff0c;ohos.web.webview提供web控制能…...

【Linux】学习-深入了解文件的读与写

深入了解语言级别(C语言)文件操作的"读"与"写" 在学习前&#xff0c;我们先要知道在Linux下的一个原则&#xff1a;一切皆是文件 如何理解呢&#xff1f;举个外设的例子&#xff0c;比如键盘和显示器&#xff0c;这两个外设也可以其实本质上也是文件&…...

java实战:销售订单30分钟未支付自动取消

本文将介绍如何使用Java实现一个销售订单在30分钟内未支付则自动取消的系统。我们将探讨如何使用Spring的定时任务功能来检查订单状态&#xff0c;并在订单未支付的情况下执行取消操作。 一、需求分析 在电商系统中&#xff0c;为了管理库存和避免长时间占用资源&#xff0c;…...

一、西瓜书——绪论

第一章 绪论 1.独立同分布 通常 假设 样本空间 中 全 体样 本 服 从 一 个 未 知 “ 分 布 ” ( d i s t r i b u t i o n ) D , 我们获得的每个样本都是独立地从这个分布上采样获得的&#xff0c; 即 “ 独 立同 分布 ” ( i n d e p e n d e n t a n d i d e n t ic a …...

如何连接ChatGPT?无需科学上网,使用官方GPT教程

随着AI的发展&#xff0c;ChatGPT也越来越强大了。 它可以帮你做你能想到的几乎任何事情&#xff0c;妥妥的生产力工具。 然而&#xff0c;对于许多国内的用户来说&#xff0c;并不能直接使用ChatGPT&#xff0c;不过没关系&#xff0c;我最近发现了一个可以直接免科学上网连…...

qt学习:串口

头文件 #include <QSerialPort> #include <QSerialPortInfo> 模块 QT core gui serialport 编程步骤 配置一个ui界面&#xff0c;五个QComboBox和一个按钮和一个QTextEdit 添加一个成员 private:QSerialPort *serial; 在构造函数中初始化ui端口列表和…...

145. 二叉树的后序遍历

给你一棵二叉树的根节点 root &#xff0c;返回其节点值的 后序遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[3,2,1]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xff1a;root [1] 输…...

Postgresql 的编译安装与包管理安装, 全发行版 Linux 通用

博客原文 文章目录 实验环境信息编译安装获取安装包环境依赖编译安装安装 contrib 下工具代码 创建用户创建数据目录设置开机自启动启动数据库常用运维操作 apt 安装更新源安装 postgresql开机自启修改配置修改密码 实验环境信息 Ubuntu 20.04Postgre 16.1 编译安装 获取安装…...

【Java EE初阶十】多线程进阶二(CAS等)

1. 关于CAS CAS: 全称Compare and swap&#xff0c;字面意思:”比较并交换“&#xff0c;且比较交换的是寄存器和内存&#xff1b; 一个 CAS 涉及到以下操作&#xff1a; 下面通过语法来进一步进项说明&#xff1a; 下面有一个内存M&#xff0c;和两个寄存器A,B; CAS(M,A,B)&am…...

与AI对话:编写高效Prompt的指南

与AI对话&#xff1a;编写高效Prompt的指南 一、明确目标 引导AI提供特定格式或内容答案的策略一、明确需求二、使用示例三、设置参数四、分步询问五、使用关键词 利用关键词引导AI重点关注核心内容的技巧一、确定关键概念二、使用专业术语三、强调重要性四、避免相关术语的混淆…...

QML用ListView实现带section的GridView

QML自带的GridView只能定义delegate&#xff0c;没有section&#xff0c;类似手机相册带时间分组标签的样式就没法做。最简单的方式就是组合ListViewGridView&#xff0c;或者ListViewFlow&#xff0c;但是嵌套View时&#xff0c;子级View一般是完全展开的&#xff0c;只显示该…...

docker之程序镜像的制作

目录 一、每种资源的预安装&#xff08;基础&#xff09; 安装 nginx安装 redis 二、dockerfile文件制作&#xff08;基础&#xff09; 打包 redis 镜像 创建镜像制作空间制作dockerfile 打包 nginx 镜像 三、创建组合镜像&#xff08;方式一&#xff09; 生成centos容器并…...

Git - 每次 git pull/push 时需要账号和密码解决方案

问题描述 在提交项目代码或者拉取代码的时候&#xff0c;每次 git 都要输入用户名密码&#xff0c;很烦~ 解决方案 让服务器记下来用户名和密码&#xff0c;此时输入一次&#xff0c;以后再 git push /pull 的时候就不用再输账号和密码了 # 配置 git 记录用户名和密码 git c…...

C语言中在main函数之后运行的函数

在 Linux 平台上&#xff0c;atexit 函数同样是一个用于注册终止处理函数的库函数&#xff0c;它是 C 标准库 <stdlib.h> 的一部分。atexit 函数允许你注册一个或多个函数&#xff0c;这些函数会在 main 函数执行结束后&#xff0c;或者在调用 exit 函数时&#xff0c;由…...

pytorch训练指标记录之tensoboard,wandb

详解Tensorboard及使用教程_tensorboard怎么用-CSDN博客文章浏览阅读5.1w次&#xff0c;点赞109次&#xff0c;收藏456次。目录一、什么是Tensorboard二、配置Tensorboard环境要求安装三、Tensorboard的使用使用各种add方法记录数据单条曲线(scalar)多条曲线(scalars)直方图(hi…...

C语言——oj刷题——实现字符串逆序

当我们需要逆序一个字符串的内容时&#xff0c;可以通过C语言编写一个函数来实现。下面将详细介绍如何通过C语言实现这个功能&#xff0c;并附上代码示例。 1、实现原理 要逆序一个字符串的内容&#xff0c;可以使用两个指针来交换字符串中对应位置的字符。具体实现原理如下&am…...

空气质量预测 | Matlab实现基于SVR支持向量机回归的空气质量预测模型

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 政府机构使用空气质量指数 (AQI) 向公众传达当前空气污染程度或预测空气污染程度。 随着 AQI 的上升,公共卫生风险也会增加。 不同国家有自己的空气质量指数,对应不同国家的空气质量标准。 基于支持向量机(Su…...

Vue中的请求拦截器

目录 1 前言 2 使用方法 2.1 创建拦截器 2.2 引入拦截器 1 前言 我们常常会使用JWT令牌来验证登录&#xff0c;因此很多请求都需要携带JWT令牌&#xff0c;我们当然可以用{headers:{Authorization:xx}}的方式&#xff0c;向每个请求中都以这样的方式添加JWT令牌。不过这样…...

Java奠基】对象数组练习

目录 商品对象信息获取 商品对象信息输入 商品对象信息计算 商品对象信息统计 学生数据管理实现 商品对象信息获取 题目要求是这样的&#xff1a; 定义数组存储3个商品对象。 商品的属性&#xff1a;商品的id&#xff0c;名字&#xff0c;价格&#xff0c;库存。 创建三个…...

排序算法---快速排序

原创不易&#xff0c;转载请注明出处。欢迎点赞收藏~ 快速排序是一种常用的排序算法&#xff0c;采用分治的策略来进行排序。它的基本思想是选取一个元素作为基准&#xff08;通常是数组中的第一个元素&#xff09;&#xff0c;然后将数组分割成两部分&#xff0c;其中一部分的…...

算法||实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度

实现典型数据结构的查找、添加和删除数据 并分析其时间和空间复杂度 线性结构&#xff1a; 数组&#xff1a;是一种线性表数据结构&#xff0c;它用一组连续的内存空间&#xff0c;来存储一组具有相同类型的数据。 查找数据 &#xff1a;随机访问 流程图 /** 查询元素下标…...

【蓝桥杯冲冲冲】Invasion of the Milkweed G

【蓝桥杯冲冲冲】Invasion of the Milkweed G 蓝桥杯备赛 | 洛谷做题打卡day30 文章目录 蓝桥杯备赛 | 洛谷做题打卡day30[USACO09OCT] Invasion of the Milkweed G题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 题解代码我的一些话 [USACO09OCT] Invasion of the Mi…...

【JAVA WEB】 百度热榜实现 新闻页面 Chrome 调试工具

目录 百度热榜 新闻页面 Chrome 调试工具 --查看css属性 打开调试工具的方式 标签页含义 百度热榜 实现效果&#xff1a; 实现代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"vi…...

Linux——动静态库

基础知识:动vs静 类型动静加载时机运行时编译时可复用性多个文件只需要加载一份库文件每个文件都需要加载一份文件性能链接次数越多越有优势链接次数越少越有优势 代码编写 静态库 生成静态库 libmath.a:add.o sub.oar -rc $ $^%.o:%.cgcc -c $<使用静态库 头文件和工…...

Vulnhub靶机:hacksudo-search

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;hacksudo-search&#xff08;10.0.2.50&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.co…...

Leetcode 188 买卖股票的最佳时机 IV

题意理解&#xff1a; 给你一个整数数组 prices 和一个整数 k &#xff0c;其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说&#xff0c;你最多可以买 k 次&#xff0c;卖 k 次。 注意&#xf…...

win32编程系统BUG(Win32 API中的WM_SETTEXT消息)

由于频繁使用Win32 API中的WM_SETTEXT消息&#xff0c;导致内存占用直线上升。 暂未找到有效解决方案。...

Linux防火墙开放

记录一次问题 写的网络服务无法通信 代码没问题&#xff0c;IP绑定、端口绑定没问题&#xff0c;就是无法进行通信&#xff0c;这里要分2步走。 服务器控制台开放 进入防火墙 添加规则&#xff0c;这里以开放udp的8899端口为例 这里在服务器后台就已经开放了&#xff0c;但此时…...

通过 docker-compose 部署 Flink

概要 通过 docker-compose 以 Session Mode 部署 flink 前置依赖 Docker、docker-composeflink 客户端docker-compose.yml version: "2.2" services:jobmanager:image: flink:1.17.2ports:- "8081:8081"command: jobmanagervolumes:- ${PWD}/checkpoin…...

HarmonyOS ArkTS修改App的默认加载的界面(二十)

前言&#xff1a;在Android开发中想要修改默认启动页&#xff0c;只需要在AndroidManifest.xml中设置即可 只需要在启动的activity种添加如下属性即可 <intent-filter><action android:name"android.intent.action.MAIN" /><category android:name&qu…...

【前端高频面试题--Vue基础篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;前端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac;前端高频面试题--Vue基础篇 Vue基本原理双向绑定与MVVM模型Vue的优点计算属性与监听属性计算属性监…...