MongoDB快速copy笔记
1、聚合 》》 管道的概念和使用,和Java代码的查询实现
2、BasicQuery、BasicDBObject、BasicDBList(有一种:criteriaVar.andOperator(criteriaList.toArray(new Criteria[0]));)
3、用Query对象 实现 mongo只返回查询需要的字段,在过滤一些很长的字段时,对性能提示较大
DBObject dbObject = new BasicDBObject(); BasicDBObject fieldsObject = new BasicDBObject(); fieldsObject.put("_id", true); fieldsObject.put("imsiList", true); fieldsObject.put("fidList", true); Query query = new BasicQuery(dbObject, fieldsObject); //查询imsiList长度不为0的 query.addCriteria(Criteria.where("imsiList").not().size(0)); //。。。
4、mongo in 查询
eg:db.getCollection('faceImage').find({"personIds": {$in: ["5d4d4027d7978958761f36ee"]}})
注意:$in 包含在一对 大括号 里面,in 的条件是个 集合,包含在 一对 中括号 里面
5、mongo null 查询
(1)为null或者不存在:db.test.find({"test": null});
(2)不为null并且存在记录:db.test.find({"test":{"$ne": null}}); 或 db.test.find({"test":{"$ne": null, $exists: true}});
(3)存在:db.test.find({"test":{$exists: true}});
(4)不存在(不会返回null的值):db.test.find({"test":{$exists: false}});
(5)存在且不为null,不为""(空字符串)
List testList = new ArrayList<>();
testList .add(null);
testList .add("");
queryUser.put("test", new BasicDBObject("$nin", testList));
6、一次1.8w数据量更新,参考如下:
场景:男女性别字段的type值(0为男,1为女),导入时反了,现要纠正这1.8万条数据
以下是比较稳妥的一种方式:比如先将 男type 全更新为9,再将女type改为正确的1,最后将男的改成0
db.getCollection('dispositionTask').find({}) //批量更新 db.getCollection('dispositionTask').find({"taskStatus": "FINISH"}).forEach( function(item){ db.getCollection('dispositionTask').update({"_id": item._id},{$set: {"remark": 666}}) } ) //统计数量 db.getCollection('dispositionTask').find({"taskStatus": "FINISH"}).count({})
7、最常见的 当前时间 表示法
//1 字符串 转 Date new Date("2019-10-24T10:27:26.312Z") //ISODate("2019-10-24T10:27:26.312Z") //2 Date 转 时间戳 new Date().valueOf() //1571885450433 //3 时间戳 转 Date new Date(1570515930*1000) //ISODate("2019-10-08T06:25:30Z") //-->根据输入的 字符串时间 换成 时间戳,且以秒 new Date("2019-10-24T10:27:26").valueOf() / 1000 //1571884046
8、修改字段名称
db.keyPerson.updateMany(
{"blackClassId": "liuzhou20190926"},
{$rename: {"gender": "sex"}},
false,
true
)
第一个{}是过滤条件,可以为空
$rename:是关键
第三个false表示:若根据该过滤条件无法找到匹配的文档时,不插入该文档
第四个true表示:更新多条
8、将一个字段的值 更新到 另外一个字段上
db.getCollection('dispositionTask').find({}).forEach( function(doc){ doc.new_task_name = doc.taskName + "_back"; doc.placeList.forEach(function(item){ item.new_pname = item.placeName + "_jbjx"; }) db.dispositionTask.save(doc); } )
其中,placeList是原文档里面的集合字段,更新集合里面的字段要再forEach一次
8、根据字段长度统计数据
db.car.find({$where:"this.carLicense.length >= 8"}).count() db.car.find({"carLicense": {$exists: true, $regex: /^.{8,}$/}}).count()
在car表6w数据时,第一种方式平均要1.2s,而第二种只要0.1s多
8、删除 或 新增 字段名
db.student.update( {"age": {$lte: 30}}, {$set: {mobilePhone: "123456"}}, false, true )
第一行:查询条件,可置空{}
第二行:$set,新增 mobilePhone字段,紧跟后面的是初始值
第三行:false表示批量,true的话,只会执行匹配到的第一条记录
第四行:false表示如果没有匹配到任何记录的话,会新增一条新的记录;true则不会
批量删除字段
db.student.update( {"age": {$lte: 30}}, {$unset: {"mobilePhone": "phone"}}, true, false )
第一行:查询条件,可置空{}
第二行:$unset,删除 mobilePhone字段,紧跟后面的值不生效,所以用 “” 占位就行
三、四行同新增
有一点,如果第三行设置成true,表示只需要执行匹配到的第一条记录,
如果重复执行该语句的话,不会递推修改匹配到第二条记录的值,即 即使 无限重复执行,也只有第一条匹配到的记录受影响
9、选择mongo查询返回的字段(一些导出数据场景下会用到)
//0:指定不导出的字段,默认id是导出的
db.getCollection('person').find({}, {"_id": 0, "faceUrl": 1, "imsiList": 1, "fidList": 1, "quality": 1, "createTime": 1})
//可以选择只导出 子对象 的选定字段,也可以选择 子集合 导出指定index的数据 db.getCollection('person').find({}, {"_id": 0, "faceUrl": 1, "fidList": 1, "imsiList": {$slice: 1}, "imsiList.imsi": 1, "imsiList.weight": 1, "quality": 1, "createTime": 1})
10、管道简单查询
db.keyPerson.aggregate( [ {$project: {"_id": 0, "name": 1, "age": 1}}, {$match: {"name": {$regex: /松/}}}, //{$group: {_id: "$name", num_count: {$sum: 1}}}, {$group: {_id: "$name", avg_age: {$avg: "$age"}}} ] )
$project:需要返回的字段
$match:过滤条件
聚合查询参考:https://www.cnblogs.com/zhoujie/p/mongo1.html
根据车牌统计,并且按数量排序,取前100条:
数据量是1500w的量,不加 allowDiskUse 选项会报 16945 的错
db.car.aggregate( [ {$group: {_id: "$carLicense", count: {$sum: 1}}}, {$sort: {"count": -1}}, {$limit: 100} ], {allowDiskUse: true} )
多字段排序:
db.car.aggregate([ //"$match":{"create_time": { $gte:1547049600000, $lte:1547135999000} }}, { "$group":{ "_id":{ "carLicense": "$carLicense", //"color": "$color", //"type": "$type" }, "carNum":{$sum:1} } }, {$sort: {"carNum": -1}} ]);
更多参考:https://xuexiyuan.cn/article/detail/115.html
附:子集合 聚合查询
其中person数据结构大概如下:
person: {“id”: "asd", "quality": 1, "imsiList": [{"imsi": "1222", "fnIn": 4}, {"imsi": "2111", "fnIn": 5}]}
db.person.aggregate([ {$match: {"quality": 1, "_id" : ObjectId("5dc7a3dca75882022d3ba59a")}}, {$unwind: "$imsiList"}, //统计imsiList下,fnIn的总数 {$group: {_id: "AAA", summmm: {$sum: "$imsiList.fnIn"}}} ]) db.person.aggregate([ {$match: {"quality": 1, "_id" : ObjectId("5dc7a3dca75882022d3ba59a")}}, {$unwind: "$imsiList"}, //{$project: {"imsiList": true, "endAge": true}}, {$group: {_id: "$imsiList.fnIn", count: {$sum: 1}}}, {$group: {_id: null, imsi不重复数量: {$sum: 1}}} ])
==》重复字段查询 & 删除重复数据
根据 cameraCode 分组统计,记录 重复的id,显示重复数量大于1的数据,并且大数据量下,运行在磁盘操作
db.camera.aggregate([ { $group: { _id: {cameraCode: '$key'}, count: {$sum: 1}, dups: {$addToSet: '$_id'} } }, { $match: {count: {$gt: 1}} } ], {allowDiskUse: true} )
接下来,是根据条件删除重复的数据
db.delDupKey.aggregate([ { $group: { _id: {pipe_id: '$key'}, count: {$sum: 1}, dups: {$addToSet: '$_id'} } }, { $match: {count: {$gt: 1}} } ], {allowDiskUse: true} ).forEach(function(doc) { doc.dups.shift(); db.camera.remove({ _id: { $in: doc.dups }, "type": 2 }); })
即删除重复数据里面 type=2 的数据;doc.dups.shift()后移一位,后面按id删除
11、distinct查询 以及 管道方式
//distinct第一个是要去重的字段,{}里面是过滤条件,注意length db.camera.distinct("placeName", {"groupId" : "1"}).length db.camera.aggregate([ {$match: {"groupId" : "1"}}, {$project: {"placeName": true, "name": true}}, {$group: {_id: "$placeName", count: {$sum: 1}}}, //第二次$group效果就和上面distinct一样了 {$group: {_id: null, count: {$sum: 1}}}, //如果不想显示_id,可以再加一层$project {$project: {"count": true, "_id": false}}, ])
length:统计distinct的数量直接是这样,不需要加()
使用这种方法查询时,查询的结果集大于16M 时会查询失败,失败信息如下:
{“message” : “distinct failed: MongoError: distinct too big, 16mb cap”,”stack” : “script:1:20”}
使用管道这种查询方式,数据量大时就不会出现如上查询失败的情况,而且这种查询不管是内存消耗还是时间消耗都优于上面一种查询
12、mongo的简单连表查询
db.imsiRecord.aggregate([ { $lookup: { from: "camera", localField: "placeId", foreignField: "placeId", as: "exist_in_imsi" } } ])
大概就是:select * from imsiRecord ir left join camera c where ir.placeId = c.placeId
但是,结果集会在一个集合里面,集合名称就是我们自定义:exist_in_imsi
==>如何实现:存在imsiRecord表,但是 不存在 于 camera表,类似 select * from imsiRecord where placeId not in(select placeId from camera) 的功能?
db.imsiRecord.aggregate([ { $lookup: { from: "camera", localField: "placeId", foreignField: "placeId", as: "exist_in_imsi" } }, { $match : {"exist_in_imsi" : []} } ])
简单的利用mongo的管道功能就OK了,当然,更多的还是需要看官网!
13、批量替换某字段 里面 的 指定字符
db.keyPerson.find({ //'_id': ObjectId("5ddf7711da5015660d5998ab"), 'faceUrl': { '$ne': null }, 'faceUrl': /^http:\/\/192.168.31.228:8090/ }).forEach(function(item) { var tmp = String(item.faceUrl) //var tmp1 = tmp.match(/^http:\/\/116.213.206.162/) if (tmp == null) { print(item.faceUrl) } else { tmp = tmp.replace(/^http:\/\/192.168.31.228:8090/g, "http://192.168.31.228:8099"); } item.faceUrl = tmp; db.getCollection('keyPerson').save(item); });