MongoDB常用SQL
基本查询
简单查询
find({"comId":"1012", "group":123})
包含:$in
find({name: {$in:["zhangsan", "lisi"]}}) #in是匹配[]中任意一个值即可 find({name: {$in:[/^zh/, /si$/]}}) #查询name字段中,以zh开头或者si结尾的 find({"procTree.pid": {$in: [2011, 2012]}}) #当字段为一个数组时,数组中的任一元素匹配[]中任意一个值即可
匹配所有:$all
find({"procTree.pid": {$all: [2011, 2012]}}) #all是必须匹配[]中所有值才算匹配
范围查询
操作符:$gte:大于等于 $lte:小于等于 $eq:等于
find({age: {$lt:20}}) #查询age<20的 find({age: {$gt:20}}) #查询age>20的 find({tags: {$ne:null}}) #查询tags不为空的
数组中元素个数:$size
find({"procTree": {$size: 2}}) #查询procTree数组中有两个元素的
字段是否存在:exists
find({"detections.detectionName":{$exists:true}}) #子文档中的detectionName字段是否存在 find({"elementIds.3":{$exists:true}}) #文档中的elementIds字段(数组)索引为的3的数据是否存在(等效于数组长度是否>3)
模糊查询
find({"comId":/11/}) #查询comId包含11的(全模糊匹配) find({"comId":/^11/}) #前缀匹配 find({"comId":{$regex:"^11"}}) #前缀匹配 find({"comId":{$regex:"12$", $options:"i"}}) #后缀匹配,忽略大小写
“非”查询:$not、$nin
$not后面只能接表达式,比如正则、$gt等
find({name: {$not: /zhang/}}) #查询name字段,不是以zhang开头的 find({age: {$not: {$gt: 30}}}) #查询age字段,不是大于30的,即小于等于30的 find({name: {$nin: [/^zh/, /si$/]}}) #查询name字段,不是以zh开头或者si结尾的 find({name: {$nin: ["zhangsan", "lisi"]}}) #查询name字段,既不等于zhangsan也不等于lisi的
投影查询
find({"comId":{$regex:"12$"}, {"comId":1, "detectType":1, "group":1, "_id":0}) #1表示显示,0表示不显示。_id默认显示,要不想显示,需要设置为0
分页查询
find().skip(3).limit(2) #跳过3条数据,然后返回最多2条数据
查询数据条数:count()
find({"comId":"1012"}).count() #返回find过滤后的数据条数 find({"comId":"1012"}).limit(2).count(true) #返回2条数据。使用了limit的sql,如果直接使用count(),里面参数不是true或者非0的,那返回的结果就是全部的(跟上面一样)
排序:sort()
find({"comId":"1012"}).sort({"createTime":-1}) #根据createTime排序。1是升序,-1是降序
去重:distinct()
distinct("modelType")
聚合查询
计数、求和:$sum
aggregate([{$group: {_id:null, cou: {$sum:1}}}]) #相当于select count(*) cou from dev aggregate([{$group: {_id:null, totalAge: {$sum:"$age"}}}]) #相当于select sum(age) totalAge from dev aggregate([{$group: {_id:"$name", totalAge: {$sum:"$age"}}}]) #相当于select name _id, sum(age) totalAge from dev group by name
条件筛选:$match
aggregate([{$match: {age: {$gt:20}}}, {$group: {_id:null, cou: {$sum:1}}}]) #相当于select count(*) cou from dev where age>20 aggregate([{$group: {_id:"$name", totalAge: {$sum:"$age"}}}, {$match:{totalAge: {$gt:25}}}]) #相当于select name _id, sum(age) totalAge from dev group by name having totalAge>25
最大、最小、平均值:$max、$min、$avg
aggregate([{$group: {_id:null, maxAge: {$max:"$age"}}}]) #相当于select max(age) maxAge from dev aggregate([{$group: {_id:null, minAge: {$min:"$age"}}}]) #相当于select min(age) minAge from dev aggregate([{$group: {_id:null, avgAge: {$avg:"$age"}}}]) #相当于select avg(age) avgAge from dev
统计结果返回数组:$push、$addToSet
aggregate([{$group: {_id:"$name", ageArr: {$push:"$age"}}}]) #按照name分组,使用数组返回组内所有的age值(不会去重) aggregate([{$group: {_id:"$name", ageArr: {$addToSet:"$age"}}}]) #按照name分组,使用数组返回组内所有的age值(会去重)
数组字段拆分:$unwind
注意:文档中没有tags字段的不会显示
aggregate([{$unwind:"$tags"}]) #将集合中所有tags字段(数组)拆分成单个显示。比如某个文档中tags有三个元素,则会拆分成三条数据
聚合投影查询 $project
注意:$project后面的投影查询,字段后面既可以使用变量(如"$name"),也可以使用数字(0或1)。不过,如果想字段名称取别名,则只能使用变量
aggregate([{$unwind:"$tags"}, {$project:{_id:0,name:"$name", tags:1}}]) #将tags数组的内容拆分,并只显示name和tags键
字符串操作 $project
注意:$concat中的字段变量只能为字符串类型
aggregate([{$project:{_id:0, Name:{$toUpper:"$name"}}}]) #name的值转为大写并取别名Name。转小写:toLower aggregate([{$unwind:"$tags"}, {$project:{_id:0, nameTags:{$concat:["$name", "-", "$tags"]}}}]) #将tags数组的内容拆分,name字段和tags字段拼接并取别名nameTags aggregate([{$project:{_id:0, namePrefix:{$substr:["$name", 0, 3]}}}]) #只显示name字段前三个字符并取别名namePrefix
关联查询 $lookup
场景:关联查询多个表。主表qtasset_mo_host,外键_agent_id、从表app_ids_config,关联键agent_id
db.qtasset_mo_host.aggregate([ {$lookup: { from: "app_ids_config", localField: "_agent_id", foreignField: "agent_id", as: "config" }}, {$match:{ _agent_id:{$in: ["a245382b8bc9c82c", "b69cfdffb5b1a99f", "ad4b436be13ae367", "2301fc678fd53886"]}, "config.host_enable": true }}, {$project : { _agent_id: 1, "config.host_enable": 1 }} ])
日期处理
MongoDB中的时间会比系统当前时间少8h。因为默认是UTC时区,而中国的时区是东八区,比UTC快8h
插入日期
insert({time:new Date()}) #会比系统时间少8h insert({time:new Date("2020-05-20T10:30:46Z")}) #必须严格按照日期格式,否则插入的日期或时间会有问题 insert({time:ISODate("2020-05-20 10:30:46")}) #ISODate为内置函数,不用完全按照上面的日期格式,它会自动转成正确的
日期过滤查询
find({time: {$eq:new Date("2020-05-20T10:30:46Z")}}) find({time: {$gt:ISODate("2020-05-20 10:30:46")}})
日期处理查询
注意:$dayOfWeek:星期日为1,星期六为7,$week:计数从0开始
aggregate([{$match:{time: {$ne:null}}},{$project:{year:{$year:"$time"}, month:{$month:"$time"}, day:{$dayOfMonth:"$time"}}}]) #查询time字段的年、月、日 aggregate([{$match:{time: {$ne:null}}},{$project:{hour:{$hour:"$time"}, minute:{$minute:"$time"}, second:{$second:"$time"}, milSec:{$millisecond:"$time"}}}]) #查询time字段的时、分、秒、毫秒 aggregate([{$match:{time: {$ne:null}}},{$project:{星期:{$dayOfWeek:"$time"}, 全年第几周:{$week:"$time"}, 全年第几天:{$dayOfYear:"$time"}}}])
显示自定义日期格式
aggregate([{$match:{time: {$ne:null}}},{$project:{myTime:{$dateToString:{format:"%Y-%m-%d %H:%M%S", date:"$time"}}}}])
新增
语法格式:insert({新增数据})
insert({"_id":1, name:"zhangsan"}) #单条新增 insertMany([{"_id":1, name:"zhangsan"}, {"_id":2, name:"lisi"}]) #批量新增
更新
语法格式:update({查询条件}, {更新内容}, {更新参数(可选)})
update({name:"zhangsan"},{age:100}) #文档更新成指定的内容,即更新后文档只有_id和age字段了 update({name:"zhangsan"},{$inc:{age:1}}) #将age的值+1 update({name:"zhangsan"},{$set: {age:200}}) #加了$set,更新或新增指定字段,不会改变文档其它字段(只会更新第一条数据) update({name:"zhangsan"},{$set: {age:200}}, {multi:true}) #批量更新或新增指定字段 update({name:"zhangsan"},{$unset: {age:1}}, {multi:true}) #加了$unset,删除指定字段,不会改变文档其它字段
引用自身的值进行更新
场景:将docker_detect_dashboard中createTime字段的值缩小1000倍
db.docker_detect_dashboard.find({"modelType":"abnormal_model_event", "createTime":{$gt: NumberLong("10000000000")}}).forEach(function(i){ db.docker_detect_dashboard.update({_id: i._id}, {$set: {"createTime": i.createTime/1000}} ) })
两个表联表查询,然后更新其中一个表的字段
场景:detections 为表 ids_incident 的子文档,当 ids_incident 和 ids_detection 中的条件匹配上时,更新 ids_incident.detections 子文档,为子文档增加字段 detectionName 并对其赋值
db.ids_detection.find({comId:"123456"}).forEach(function(i){ db.ids_incident.updateMany({incidentId: i.incidentId, "detections.detectionId":i.detectionId}, {$set: {"detections.$.detectionName": i.detectionName}}) })
修改字段名称
db.ids_detection.update({}, {$rename:{"filePath":"fpath"}}, false, true) #将filePath改为fpath
查询并修复,原子操作
场景:需要先查询 ids_scan_result 中 totalReportTimes 字段,然后再对该字段进行计算(比如+1操作),最后再赋值给该字段,整个过程一定要串行执行(也可以通过加锁实现)
db.ids_scan_result.findAndModify({ query:{ baseJobId:"98e7a9f1-aa59-4af4-9310-6c8e794643c8" }, update: { $inc: {totalReportTimes: 1}, $set: {actualReportTimes: 2} } })
修改嵌套子文档数组中的多个元素
场景:app_ids_detect_config 中 method_switches 字段为一个对象数组,对象中只有两个字段 detection_method 和 enable ,需要将 detection_method 的值为 CTN 前缀的元素的enable改为true
db.app_ids_detect_config.updateMany({ "platform_type": { $in: ["LINUX_SERVER", "LINUX_PC"] }, "method_switches.detection_method": /CTN/ }, { $set: { "method_switches.$[a].enable": true } }, { arrayFilters: [{ "a.detection_method": /CTN/ }] })
说明:如果只需要修改数组中的一个元素,那可以不需要arrayFilters,如果需要修改多个元素,则必须加上arrayFilters。arrayFilters过滤后的值将作为上面$set中的下标。
删除
语法格式:remove({查询条件})
remove({}) #删除所有数据 remove({name:"zhangsan"}) #删除指定条件的数据
索引
创建索引
语法格式:createIndex({创建索引的键: 排序规则}, {创建索引参数(可选)})
createIndex({name:1}) #为name创建正序索引 createIndex({name:1, age:-1},{name:"idx_name_age"}) #为name和age创建复合索引,索引名称为idx_name_age
createIndex({name:1, age:1},{name:"idx_name_age", unique:true}) #创建唯一索引
排序规则:1:正序 -1倒序
索引参数:
参数 | 数据类型 | 默认值 | 功能 |
background | Boolean | false | 后台建立索引,创建索引时不阻塞其它操作 |
expireAfterSeconds | Integer | 指定索引过期时间 | |
name | String | 指定索引名称,如果未指定,MongoDB会生成一个索引字段名称_排序顺序的名称 | |
sparse | Boolean | false | 对文档中不存在的字段数据不启用索引 |
unique | Boolean | false | 创建唯一索引 |
注意点:如果集合中字段索引已存在,再次为该字段创建索引不会生效
查看索引
getIndexes() #查看集合中所有索引信息
getIndexKeys() #查看集合中所有的索引键
totalIndexSize(可选参数) #查看集合中索引的大小,单位字节。参数说明:可选参数为0、false或者空,显示所有索引的总大小,其它值显示每个索引的大小及总大小
删除索引
dropIndex("idx_name") #删除索引名称为idx_name的索引 dropIndexes() #删除全部索引,_id键的索引除外
重建索引
使用场景:数据大量变化后,可以使用重建索引构建更高效的B-Tree,优化索引查询效率
过程说明:重建索引是删除原索引重新创建的过程,不建议反复使用
reIndex()