1.MongoDB 简介
MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
mongodb 是一种 NoSql 的数据库,与Mysql 这种关系型数据库不一样,它是一种非关系型数据库。
NoSql 常用于大规模的数据储存,例如 用户信息搜集,用户登陆信息,访问信息等,这些数据没有固定的结构,这些NoSql 都能很好的支持,无需多余操作就能方便的横向扩展。
mongodb 的储存结构是 二进制的 json。
NoSql 的优缺点:
优点:
- - 高可扩展性
- - 分布式计算
- - 低成本
- - 架构的灵活性,半结构化数据
- - 没有复杂的关系
缺点:
- - 没有标准化
- - 有限的查询功能(到目前为止)
- - 最终一致是不直观的程序
2.NoSQL 数据库分类
类型 | 部分代表 | 特点 |
---|---|---|
列存储 | HbaseCassandraHypertable | 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。 |
文档存储 | MongoDBCouchDB | 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。 |
key-value存储 | Tokyo Cabinet / TyrantBerkeley DBMemcacheDBRedis | 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能) |
图存储 | Neo4JFlockDB | 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。 |
对象存储 | db4oVersant | 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。 |
xml数据库 | Berkeley DB XMLBaseX | 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。 |
3.MongoDB 与 Mysql 对比
Mysql | MongoDB | 解释 |
---|---|---|
DataBase | DataBase | 数据库 |
table | collection | 表/集合 |
row | document/BSON | 数据行/文档 |
column | field | 列/域 |
index | index | 索引 |
table joins | $lookup | 多表关联,3.2 版本新增🤩 |
primary key | primary id | 主键,mongo 的 _id 为默认主键 |
4 MongDb 操作语法
4.1 创建集合
//创建集合
db.createCollection("test1")
//插入文档同时创建文档
db.test2.insert({"name":"test1"})
db.createCollection("mycol", {
capped: true,
autoIndexId: true,
size:
6142800,//指定字节数,超过后覆盖
max: 10000 //指定最大文档数,超过后覆盖
})
4.2 删除集合
//test1 为 集合名称
db.test1.drop()
4.3 插入文档
文档的数据结构跟json 数据结构一致,文档在mongodb 中存储的结构为BJSON。
BJSON 是一种二进制的JSON,Binary Json 简称 BJSON。
语法如下:
db.COLLECTION_NAME.save( document ) // 最新版本已废除
主键重复则报错,options 支持指定写入策略( writeConcern 默认 1,要求确认写操作)及写入顺序( ordered 默认true)。
db.COLLECTION_NAME.insert( document,options )
db.test2.insert({"_id":"2","name":"那么多2"})
db.test2.insertOne({"_id":"3","name":"那么多2"})
db.test2.insertMany([
{"name":"111111b","age":100},
{"name":"222222b","age":199},
{"name":"333333b","age":100},
{"name":"444444b","age":199},
{"name":"555555b","age":100},
{"name":"666666b","age":199}
])
4.4 更新文档
update 方法用于已更存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
使用案例:
//搜索 name=111111b 的 修改 name 为 ak47,默认只会修改符合条件的第一条数据,如需修改多条 则需要设置 multi 参数为 true。
db.test2.update({"name":"111111b"},{$set:{"name":"ak47"}})
// multi 使用案例
db.test2.update({"name":"test1"},{$set:{"name":"ak47"}},{multi:true})
只更新第一条记录:
db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } );
全部更新:
db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true );
只添加第一条:
db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false );
全部添加进去:
db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true );
全部更新:
db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true );
只更新第一条记录:
db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );
4.5 删除文档
语法格式:
db.collection.remove(<query>,{justOne:<boolean>,writeConcern:<document>})
使用案例
删除 name = ak47 的,只删除一个,justOne 默认为 false 则删除所有。
db.test2.remove({name : "ak47"},{justOne:true})
4.6 查询文档
语法格式:
db.collection.find(query, projection)
//只返回一个文档
db.collection.findOne(query, projection)
- query :可选,使用查询操作符指定查询条件
- projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
使用案例:
//查询 name = test1 的,且只查询 name(此处为 projection)
db.test2.find({name:"test1"},{name:1})
// AND 条件用法
db.test2.find({name:"sssssssd",age:100})
// OR 条件用法,此处 $or 使用了 查询选择器
db.test2.find({$or:[{name:"sssssssd"},{age:199}]})
// OR 和 AND 条件连用,此处 $or 使用了 查询选择器
db.test2.find({name:"666666a",$or:[{name:"sssssssd"},{age:199}]})
4.7 查询选择器和投影运算符
比较选择器
名字 描述
$eq 判断相等
$gt 判断大于
$gte 判断大于等于
$in 判断在其中
$lt 判断小于
$lte 判断小于等于
$ne 判断所有值都不等于指定值
$nin 判断不在其中
db.student.insertMany([
{name:"a1",age:18,sex:0},
{name:"b1",age:23,sex:1},
{name:"c1",age:44,sex:0},
{name:"d1",age:66,sex:0},
{name:"e1",age:777,sex:1},
{name:"f1",age:11,sex:0},
{name:"g1",age:28,sex:0},
{name:"h1",age:38,sex:3},
{name:"i1",age:48,sex:0},
{name:"j1",age:58,sex:0},
{name:"k1",age:64,sex:2},
{name:"l1",age:121,sex:0},
{name:"m1",age:234,sex:1},
{name:"n1",age:346,sex:0},
{name:"o1",age:65,sex:0},
{name:"p1",age:34,sex:0},
{name:"q1",age:656,sex:3},
{name:"r1",age:678,sex:0},
{name:"s1",age:23,sex:0}
])
//查询所有
db.student.find()
//等于查询
db.student.find({name:"a1"})
db.student.find({name:{$eq:"a1"}})
//age 小于 18
db.student.find({age:{$lt:18}})
//age 小于等于 18
db.student.find({age:{$lte:18}})
//age 大于 23
db.student.find({age:{$gt:23}})
//age 大于等于 23
db.student.find({age:{$gte:23}})
//name 不等于 db
db.student.find({name:{$ne:"b1"}})
// age 在 18/23/44
db.student.find({age:{$in:[18,23,44]}})
// age 不在 18/23/44范围内 的其他数据
db.student.find({age:{$nin:[18,23,44]}})
逻辑选择器
$and 与
$not 非
$nor 异或
$or 或
// $and 并且条件查询
db.student.find({
$and: [
{
name: "a1"
},{
age: 18
}]
})
// $or 或者条件查询
db.student.find({
$or:[{name:"a1"},{age:23}]
})
//查询 age 不在大于 23 的范围内的数据
db.student.find({age:{$not:{$gt:23}}})
// 条件取反查询,如: age 不等于 23 的
db.student.find({$nor:[{age:23}]})
元素选择器
名字 描述
$exists 匹配具有指定字段的文档。
$type 如果字段属于指定类型,则选择文档
//查询 name 字段 数据类型是 string的
db.student.find({"name":{$type:'string'}})
//匹配字段是否含有 name 字段,true 表示匹配含有的,false 表示匹配没有的
db.student.find({name:{$exists:true}})
评估选择器
$expr 允许在查询语言中使用聚合表达式。
//$expr 表达式,匹配 sex 字段的值 和 age 字段值相等的
db.student.find({$expr:{$eq:["$sex","$age"]}})
//需求:qty 数量大于100 的任何商品 可以获得 50% 的折扣,否则打 25% 折扣
//准备测试数据
db.supplies.insertMany([
{ "_id" : 1, "item" : "binder", "qty" : 100 , "price" : 12 },
{ "_id" : 2, "item" : "notebook", "qty" : 200 , "price" : 8 },
{ "_id" : 3, "item" : "pencil", "qty" : 50 , "price" : 6 },
{ "_id" : 4, "item" : "eraser", "qty" : 150 , "price" : 3 },
{ "_id" : 5, "item" : "legal pad", "qty" : 42 , "price" : 10}
])
db.supplies.find()
db.supplies.find({
$expr:{
$lt:[{
$cond:{
if:{$gte:["$qty",100]},
then:{$multiply:["$price",0.50]},
else:{$multiply:["$price",0.75]}
}
},5]}
})
$jsonSchema 根据给定的JSON模式验证文档。
//定义以下示例架构对象:
let myschema = {
required: [ "item", "qty", "instock" ],
properties: {
item: { bsonType: "string" },
qty: { bsonType: "int" },
size: {
bsonType: "object",
required: [ "uom" ],
properties: {
uom: { bsonType: "string" },
h: { bsonType: "double" },
w: { bsonType: "double" }
}
},
instock: { bsonType: "bool" }
}
}
//您可以使用$jsonSchema查找集合中满足模式的所有文档:
db.inventory.find( { $jsonSchema: myschema } )
db.inventory.aggregate( [ { $match: { $jsonSchema: myschema } } ] )
$mod 对字段值执行模运算,并选择具有指定结果的文档。
// 取模,age 除以4=0 的数据
db.student.find({age:{$mod:[4,0]}})
$regex 选择值与指定正则表达式匹配的文档。
//示例将集合products与以下文档一起使用:
db.productes.insertMany(
[
{ "_id" : 100, "sku" : "abc123", "description" : "Single line description." },
{ "_id" : 101, "sku" : "abc789", "description" : "First line\nSecond line" },
{ "_id" : 102, "sku" : "xyz456", "description" : "Many spaces before line" },
{ "_id" : 103, "sku" : "xyz789", "description" : "aaaaaaa"}
]
)
//匹配sku 字段类似 "%789" 的文档
db.productes.find( { sku: { $regex: /789$/ } } )
$text 执行文本搜索。
$text运算符接受具有以下字段的文本查询文档:
Field | Type | Description |
---|---|---|
$search | string | MongoDB 解析并用于查询文本索引的术语字符串。除非指定为短语,否则 MongoDB 会对术语进行逻辑OR 搜索。有关该字段的更多信息,请参见Behavior。 |
$language | string | 可选的。确定用于搜索的停用词列表以及词干分析器和分词器规则的语言。如果未指定,则搜索使用索引的默认语言。有关支持的语言,请参见Literals 搜寻语言。 |
限制🚫
- 一个查询最多可以指定一个$text表达式。
- $text查询不能出现在$nor表达式中。
- $text查询不能出现在$elemMatch查询表达式或$elemMatch投影表达式中。
- 要在$or表达式中使用$text查询,必须对$or数组中的所有子句构建索引。
- 如果查询包含$text查询表达式,则不能使用hint()。
- 如果查询包含$text表达式,则不能指定$natural排序 Sequences。
- 您不能将需要特殊text index的$text表达式与需要不同类型的特殊索引的查询运算符组合在一起。例如,您不能将$text表达式与$near运算符组合。
- Views不支持文本搜索。
如果在聚合中使用$text运算符,则以下限制也适用。
- 包含$text的$match阶段必须是管道中的“第一”阶段。
text
运算符在该阶段只能出现一次。text
运算符表达式不能出现在$or或$not表达式中。- 默认情况下,文本搜索不会按匹配分数的 Sequences 返回匹配的文档。在$sort阶段使用$meta聚合表达式。
// $text 支持 对text index 索引的字段的内容执行文本搜索
//语法格式如下:
{
$text:
{
$search: <string>,
$language: <string>,
$caseSensitive: <boolean>, //是否区分大小写,默认false,不区分
$diacriticSensitive: <boolean>//是否区分 变音符号,默认false,不敏感
}
}
//准备测试数据
db.articles.insert(
[
{ _id: 1, subject: "coffee", author: "xyz", views: 50 },
{ _id: 2, subject: "Coffee Shopping", author: "efg", views: 5 },
{ _id: 3, subject: "Baking a cake", author: "abc", views: 90 },
{ _id: 4, subject: "baking", author: "xyz", views: 100 },
{ _id: 5, subject: "Café Con Leche", author: "abc", views: 200 },
{ _id: 6, subject: "Сырники", author: "jkl", views: 80 },
{ _id: 7, subject: "coffee and cream", author: "efg", views: 10 },
{ _id: 8, subject: "Cafe con Leche", author: "xyz", views: 10 }
]
)
//创建索引
db.articles.createIndex( { subject: "text" } )
//模糊搜索单个单词
db.articles.find( { $text: { $search: "coffee" } } )
//结果
1 coffee xyz 50
7 coffee and cream efg 10
2 Coffee Shopping efg 5
//or 方式匹配 多个单词
db.articles.find( { $text : { $search : "con leche"} })
//结果
8 Cafe con Leche xyz 10
5 Café Con Leche abc 200
//搜索短语
db.articles.find({$text : {$search : "\"and cream\""}})
//结果
7 coffee and cream efg 10
//排除单词搜索,含有 coffee 不含shop
db.articles.find({$text : {$search : "coffee -shop"}})
//结果
1 coffee xyz 50
7 coffee and cream efg 10
//搜索其他语言
db.articles.find({$text : {$search : "leche",$language:"es"}})
//结果
8 Cafe con Leche xyz 10
5 Café Con Leche abc 200
//大小写区分/变音符号敏感
//默认 不区分大小写,不敏感变音符号
db.articles.find( { $text: { $search: "сы́рники CAFÉS" } } )
//结果
8 Cafe con Leche xyz 10
5 Café Con Leche abc 200
6 Сырники jkl 80
//区分大小写搜索
db.articles.find( { $text: { $search: "Coffee", $caseSensitive: true } } )
//结果
2 Coffee Shopping efg 5
//区分大小写的 短语
db.articles.find( {$text : {$search : "\"Café Con Leche\"", $caseSensitive: true}})
//结果
5 Café Con Leche abc 200
//变音符号搜索
db.articles.find( { $text: { $search: "CAFÉ", $diacriticSensitive: true } } )
//结果
5 Café Con Leche abc 200
//文档匹配分数
db.articles.find({$text : {$search:"coffee"}},{score:{$meta:"textScore"}})
//结果
1 coffee xyz 50 1
2 Coffee Shopping efg 5 0.75
7 coffee and cream efg 10 0.75
//按匹配得分排序
db.articles.find({$text:{$search:"coffee"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}})
//结果
1 coffee xyz 50 1
2 Coffee Shopping efg 5 0.75
7 coffee and cream efg 10 0.75
//按匹配得分排序后,取前两条
db.articles.find({$text:{$search:"coffee"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}}).limit(2)
//条件查询 + $text 搜索的 排序结果
db.articles.find(
{
author: "xyz",
$text: {
$search: "coffee con"
}},
{score: {
$meta: "textScore"
}}
).sort({
date: 1,
score: {
$meta: "textScore"
}
})
//结果
1 coffee xyz 50 1
8 Cafe con Leche xyz 10 0.666666666666667
$where 匹配满足JavaScript表达式的文档。
🥶应该尽量避免使用 $where ,会对速度有很大影响,因为$where 需要把BSON 数据转化为Javascript对象,然后再通过where 条件判断,同时不能使用索引。
db.students.insertMany([{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] },{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] },{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] },{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] },{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] },{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }])//JavaScript 字符串形式db.students.find({$where:"this._id == this.semester"})//JavaScript 函数形式db.students.find({$where:function(){return this._id == this.semester;}})//JavaScript 函数形式 查询 文档中 字段相等的文档db.students.find({$where:function(){ for(var item in this){ for(var otherItem in this){ if(item != otherItem && this[item] == this[otherItem]){ return true; } } } return false;}})
地理空间选择器
$geoIntersects 选择与几何图形相交的几何图形。二维球面索引支持$geoIntersects.
$geoWithin 选择边界几何图形中的几何图形。二维球面和二维索引支持$geoWithin.
$near 返回点附近的地理空间对象。需要地理空间索引。2dsphere和2d索引支持$near。
$nearSphere 返回球体上某个点附近的地理空间对象。需要地理空间索引。2dsphere和2d indexes支持$近岸球体。
数组选择器
$all 匹配包含查询中指定的所有元素的数组。
//测试数据db.students.insertMany([{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] },{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] },{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] },{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] },{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] },{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }])db.school.insertMany([{ _id: 1, zipcode: "63109", students: [ { name: "john", school: 102, age: 10 }, { name: "jess", school: 102, age: 11 }, { name: "jeff", school: 108, age: 15 } ]},{ _id: 2, zipcode: "63110", students: [ { name: "ajax", school: 100, age: 7 }, { name: "achilles", school: 100, age: 8 }, ]},{ _id: 3, zipcode: "63109", students: [ { name: "ajax", school: 100, age: 7 }, { name: "achilles", school: 100, age: 8 }, ]},{ _id: 4, zipcode: "63109", students: [ { name: "barney", school: 102, age: 7 }, { name: "ruth", school: 102, age: 16 }, ]}])db.students.find()//匹配数组 元素值 grades 中含有 70 的db.students.find({grades:{$all:[70]}})db.school.find()//查询 students 数组中的对象 name 等于 johndb.school.find({"students.name":{$all:["john"]}})
$elemMatch 如果数组字段中的元素匹配所有指定的$elemMatch条件,则选择文档。
//测试数据来源于 上一个案例db.students.find()//grades 数组含有 大于 90 的值,则认为符合要求db.students.find({ grades : {$elemMatch:{$gt : 90}}})db.school.find()//查询 students的 age 大于 10 的文档db.school.find({ "students": { $elemMatch: { "age": {$gt:10} } }})
$size 如果数组字段是指定的大小,则选择文档。
//查询 grades 数组长度为 3 的 db.students.find({ grades:{$size:3}})//查询 students 数组长度为 3 的 db.school.find({ students:{$size:3}})
位操作选择器
$bitsAllClear 匹配数值或二进制值,其中一组位位置的值都为0。
$bitsAllSet 匹配数值或二进制值,其中一组位位置的值都为1。
$bitsAnyClear 匹配数值或二进制值,其中一组位位置中的任意一位的值为0。
$bitsAnySet 匹配数值或二进制值,其中一组位位置中的任意一位的值为1。
注释选择器
$comment 向查询谓词添加注释。
4.8投影操作
$ 投影数组中与查询条件匹配的第一个元素。
数组字段限制🚫
在处理数组投影时,MongoDB 需要满足以下条件:
- 投影文档中只能出现一个位置$运算符。
- 只有一个数组字段(该字段受$投影运算符限制)应出现在query document中。查询文档中的其他数组字段可能导致未定义的行为。
- query document仅应在要投影的数组字段上包含一个条件。多个条件可能在内部相互覆盖并导致不确定的行为。
db.students.insertMany([{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] },{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] },{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] },{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] },{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] },{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }])//查询 semester=1 and grades 大于等于85,然后只返回 符合的第一个元素db.students.find({ semester: 1, grades: { $gte: 85 }},{"grades.$":1})//测试数据db.testStudent.insertMany([ { "_id": 7, semester: 3, "grades": [{ grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 90, mean: 85, std: 3 }] }, { "_id": 8, semester: 3, "grades": [{ grade: 92, mean: 88, std: 8 }, { grade: 78, mean: 90, std: 5 }, { grade: 88, mean: 85, std: 3 }] }])//对象投影查询//查询出 gredes 中 mean 属性值 大于 70 的第一个元素db.testStudent.find({ "grades.mean": { $gt: 70 }}, { "grades.$": 1})
$elemMatch 投影数组中与指定的$elemMatch条件匹配的第一个元素。
db.school.insertMany([{ _id: 1, zipcode: "63109", students: [ { name: "john", school: 102, age: 10 }, { name: "jess", school: 102, age: 11 }, { name: "jeff", school: 108, age: 15 } ]},{ _id: 2, zipcode: "63110", students: [ { name: "ajax", school: 100, age: 7 }, { name: "achilles", school: 100, age: 8 }, ]},{ _id: 3, zipcode: "63109", students: [ { name: "ajax", school: 100, age: 7 }, { name: "achilles", school: 100, age: 8 }, ]},{ _id: 4, zipcode: "63109", students: [ { name: "barney", school: 102, age: 7 }, { name: "ruth", school: 102, age: 16 }, ]}])db.school.find()//查询 zipCode 等于 63109 的 并且 students 的对象属性 school=102 and age>10 的 第一个符合的数据 db.school.find({ zipcode: "63109"},{ students: { $elemMatch: { school: 102, age: { $gt: 10 } } }})//结果1 [ { name: "jess", school: 102, age: 11 }]3 4 [ { name: "ruth", school: 102, age: 16 }]
$meta 投射在$text操作期间分配的文档分数。
$slice 限制从数组投影的元素数量。支持跳过和限制切片。
// 控制 数组 返回的数量db.students.find({ semester: 1}, { grades: { $slice: 1 }})// 控制 数组 返回的数量,后两个db.students.find({ semester: 1}, { grades: { $slice: -2 }})// 控制 数组 返回的数量,跳过第一个,getdb.students.find({ semester: 1}, { grades: { $slice: [1,2] }})
4.9 多表联查- $lookup
测试数据:
订单集合
db.orders.insert([ { "_id" : 1, "goodsName" : "almonds", "price" : 12, "quantity" : 2 }, { "_id" : 2, "goodsName" : "pecans", "price" : 20, "quantity" : 1 }, { "_id" : 3 }])
商品库存集合
db.goods.insert([ { "_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 }])
需求:查询订单及对应商品的库存
如果 Mysql实现:
select order._id, order.goodsName, order.price, order.quantity, good.instock from orders order left join goods good on order.goodsName = good.sku
//测试数据-订单db.orders.insert([ { "_id" : 1, "goodsName" : "almonds", "price" : 12, "quantity" : 2 }, { "_id" : 2, "goodsName" : "pecans", "price" : 20, "quantity" : 1 }, { "_id" : 3 }])//测试数据-商品库存db.goods.insert([ { "_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 }])db.orders.aggregate([ { $lookup: { from: "goods",//来自goods 表 localField: "goodsName", //拿那个字段去做对比 foreignField: "sku",//对比 sku 字段 as: "goodsObj" // 同 Mysql As 用法,用于展示联查的对象数据 } }])
5.0 更新操作符
##### 5.1 $inc
$inc](https://www.docs4dev.com/docs/zh/mongodb/v3.6/reference/reference-operator-update-inc.html#up._S_inc)运算符使字段增加指定值,并具有以下形式:
{ $inc: { <field1>: <amount1>, <field2>: <amount2>, ... } }
说明:
$inc运算符接受正值和负值。
如果该字段不存在,则$inc创建该字段并将该字段设置为指定的值。
在值为空的字段上使用$inc运算符将产生错误。
$inc是单个文档中的原子操作。
//准备测试数据db.testDate.insert( { _id: 1, status: "a", lastModified: ISODate("2013-10-02T01:11:18.965Z") })db.testDate.find()//修改时间为当前时间db.testDate.update({"_id":1},{$currentDate:{lastModified:true}})//不存在的字段会自动 添加新字段并插入新值db.testDate.update({"_id":1},{$currentDate:{newTime:true}})
5.2 $min
-
$min
- $min将字段的值更新为指定的值,如果指定的值小于当前值那么修改,否则不修改。
{ $min: { <field1>: <value1>, ... } }
要在嵌入式文档或数组中指定<field>
,请使用dot notation。
说明:
如果该字段不存在,则$min运算符将该字段设置为指定的值。
为了比较不同类型的值(例如数字和空值),$min使用BSON 比较 Sequences。
5.1 管道 agreegate
聚合管道是 MongoDB 2.2版本引入的新功能。它由阶段(Stage)组成,文档在一个阶段处理完毕后,聚合管道会把处理结果传到下一个阶段。
聚合管道功能:
- 对文档进行过滤,查询出符合条件的文档
- 对文档进行变换,改变文档的输出形式
管道属于一种聚合方式,mongodb 一共提供了三种 聚合数据的方式:
- 聚合管道 - Agreegation Pipeline
- 单目的聚合操作 - Single Purpose Aggreation Operation
- MapReduce 编程模型
每个阶段用**阶段操作符(Stage Operators)定义,在每个阶段操作符中可以用表达式操作符(Expression Operators)**计算总和、平均值、拼接分割字符串等相关操作,直到每个阶段进行完成,最终返回结果,返回的结果可以直接输出,也可以存储到集合中。
5.1.1 $project
🤩首先需要先熟悉 $project 操作符的使用,它主要用于添加或删除文档中的字段,案例如下:
//准备测试数据db.testArticle.insertMany([ { "_id": ObjectId("58e1d2f0bb1bbc3245fa7570"), "title": "MongoDB Aggregate", "author": "liruihuan", "tags": ['Mongodb', 'Database', 'Query'], "pages": 5, "time": ISODate("2017-04-09T11:42:39.736Z") }, { "_id": ObjectId("58e1d2f0bb1bbc3245fa7571"), "title": "MongoDB Index", "author": "liruihuan", "tags": ['Mongodb', 'Index', 'Query'], "pages": 3, "time": ISODate("2017-04-09T11:43:39.236Z") }, { "_id": ObjectId("58e1d2f0bb1bbc3245fa7572"), "title": "MongoDB Query", "author": "eryueyang", "tags": ['Mongodb', 'Query'], "pages": 8, "time": ISODate("2017-04-09T11:44:56.276Z") }])
控制 _id 不返回,只返回 title,pages
aggregate 就是mongodb 的管道的用法,一个管道可以有多个阶段(Stage)
//不返回 _id,返回 title,pagesdb.testArticle.aggregate([{ $project: { _id: 0, title: 1, pages: 1 }}])结果:MongoDB Aggregate 5MongoDB Index 3MongoDB Query 8
5.1.2 $add 实现对数据新增 并重命名为 新的字段
//把pages ,都➕10,并命名为 新字段 new_pagesdb.testArticle.aggregate( [ { $project: { _id: 0, title: 1, pages: 1, new_pages: { $add: ["$pages", 10] } } } ])//结果MongoDB Aggregate 5 15MongoDB Index 3 13MongoDB Query 8 18
5.1.2 $group 实现 分组计算每个作者的文章数量
//得到每个作者的文章数db.testArticle.aggregate( [ { $group: { _id: "$author", total: { $sum: 1 } } } ])
5.1.3 $sort 按时间排序
//按文章发布时间排序,, 1:降序 -1:升序db.testArticle.aggregate([ { $sort: { time: - 1 } }])
5.1.4 $sort,$limit 多阶段实现先排序后限制返回数量
//先排序,再控制返回条数db.testArticle.aggregate([ { $sort: { time: - 1 } }, { $limit: 1 }])
5.1.5 $unwind 按文档中数组的个数 拆分为多条数据
注🤓:
- $unwind 参数数组字段为空或不存在时,待处理的文档将会被忽略,该文档将不会有任何输出
- $unwind 参数不是一个数组类型时,将会抛出异常
- $unwind 所作的修改,只用于输出,不能改变原文档
//拆分数组成为 多条数据db.testArticle.aggregate([ { $match: { "title": "MongoDB Query" } }, { $unwind: "$tags" }])
5.2 聚合管道查询优化
默认情况下,整个集合作为聚合管道的输入,为了提高处理数据的效率,可以使用一下策略:
- 将 $match 和 $sort 放到管道的前面,可以给集合建立索引,来提高处理数据的效率。
- 可以用 $match、$limit、$skip 对文档进行提前过滤,以减少后续处理文档的数量。
当聚合管道执行命令时,MongoDB 也会对各个阶段自动进行优化,主要包括以下几个情况:
- $sort + $match 顺序优化
如果 $match 出现在 $sort 之后,优化器会自动把 $match 放到 $sort 前面
\2. $skip + $limit 顺序优化
如果 $skip 在 $limit 之后,优化器会把 $limit 移动到 $skip 的前面,移动后 $limit的值等于原来的值加上 $skip 的值。
例如:移动前:{$skip: 10, $limit: 5},移动后:{$limit: 15, $skip: 10}
5.3 单目聚合操作
//查询作者是 xyz 的文章数量db.articles.count({"author":"xyz"})//查询 去重复后的 作者列表db.articles.distinct("author")
参考文档:https://blog.csdn.net/weixin_45958024/article/details/103928885
https://www.runoob.com/mongodb/mongodb-operators-type.html
https://www.cnblogs.com/xuliuzai/p/10055535.html
https://www.cnblogs.com/yunlongaimeng/p/11460291.html
更多参考:https://www.docs4dev.com/docs/zh/mongodb/v3.6/reference/
评论区