《MongoDB权威指南》学习笔记
https://pan.baidu.com/s/1_GJjtgW4UbUxe6hpQLX7vg
1. 简介 面向文档的数据库
以键值对的形式存储数据,区分值类型也区分大小写,数据为任意UTF-8类型
集合无模式,集合内可以存放任何类型的键值
{“foo”:1}
{“greeting”:”Hello World”}
2. 启动MongoDB mongod –config /data/etc/mongodb.conf
mongo
3. 显示数据 MongoDB shell version v3.4.23
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.23
MongoDB默认端口27017,通过访问http://127.0.0.1:27017/可以看到mongodb提供的http服务,响应数据【It looks like you are trying to access MongoDB over HTTP on the native driver port.】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 > use admin switched to db admin > > db.auth("admin" ,"admin" ) 1 > > show dbs admin 0.000GB local 0.000GB > > show tables system.users system.version > > db.system.users.find() { "_id" : "admin.admin", "userId" : BinData(4,"AhhaYxmITtC2pBqb6Qx7uw=="), "user" : "admin", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "lOBwaGzf8V7ls+Gg0gm/cw==", "storedKey" : "ajxSU13iSBweanHpvCLgipFy4Jo=", "serverKey" : "eVhwAjeYOW8zSd4A5R3ykzw7JfY=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] } > > db.system.version.find() { "_id" : "featureCompatibilityVersion", "version" : "3.4" } { "_id" : "authSchema", "currentVersion" : 5 } > > >
4. shell介绍及操作 MongoDB自带javascript shell,是功能完备的JavaScript解释器,可以运行javascript程序
也可以是函数
1 2 3 4 5 6 > function f(n){... return n*n; ... } > f(5) 25 >
shell默认连接test数据库,将数据库连接赋值给全局变量db,db 是shell访问mongodb的主要入口点
use admin 选择使用的数据库
db 显示正在使用的数据库
5. CRUD操作 5.1. 创建 数据库 use bike 会自动创建bike数据库
1 2 3 4 5 > use bike switched to db bike > db bike >
集合
1 2 3 > db.createCollection("bikes" ) { "ok" : 1 } >
集合中添加数据
1 2 3 4 5 6 7 > db.bikes.insert({ ... "id":0, ... "bikeno":100000, ... "status":0 ... }) WriteResult({ "nInserted" : 1 }) >
5.1.1. 原理和作用 数据转换成BSON传入数据库,解析BSON,检验是否包含_id,文档不超过4MB,然后把数据原样传入数据库
批量插入减少数据库校验消息请求头的开销
5.2. 查找 5.2.1. find() 1 2 3 > db.bikes.find() { "_id" : ObjectId("5da0a7a85c6be30577bc9af7"), "id" : 0, "bikeno" : 100000, "status" : 0 } >
_id 是自动创建的,相当于主键??
当_id不同时,就认为数据不同??
插入相同数据不报错
1 2 3 4 5 6 7 > db.bikes.insert({ "id" :0, "bikeno" :100000, "status" :0 }) WriteResult({ "nInserted" : 1 }) > > db.bikes.find() { "_id" : ObjectId("5da0a7a85c6be30577bc9af7"), "id" : 0, "bikeno" : 100000, "status" : 0 } { "_id" : ObjectId("5da0a9015c6be30577bc9af8"), "id" : 0, "bikeno" : 100000, "status" : 0 } >
5.2.2. findOne() 查找第一条数据 findOne()
1 2 3 4 5 6 7 8 > db.bikes.findOne() { "_id" : ObjectId("5da0a7a85c6be30577bc9af7"), "id" : 0, "bikeno" : 100000, "status" : 0 } >
5.2.3. find({}) 查找特定数据 find({查询器})
1 2 3 4 > db.bikes.find({id :0}) { "_id" : ObjectId("5da0ace75c6be30577bc9af7"), "id" : 0, "bikeno" : 100000, "status" : 0 } { "_id" : ObjectId("5da0a9015c6be30577bc9af8"), "id" : 0, "bikeno" : 100000, "status" : 0 } >
5.2.4. find({},{}) 指定返回内容
5.2.5. 操作符
$lt <
$lte <=
$gt >
$gte >=
1 > db.bikes.find({"id" :{$lt :0,$gt :3}})
$ne !=
1 > db.bikes.find({"id" :{$ne :0}})
$in 匹配多个值
$in:[]
1 > db.bikes.find({"id" :{$in :[0,1,2]}})
$nin 不匹配的所有数据
$or 多重条件
$or:[{},{}]
1 > db.bikes.find({$or :[{"id" :{$in :[0,1,2]}},{"status" :0}]})
$mod
$mod:[5,10]
查询的值/第一个给定的值 余数=第二个值
基于$not
1 > db.bikes.find({"id" :{$not :{$mod :[5,1]}}})
5.2.6. Perl兼容的正则表达式 1 > db.bikes.find({"name" :/e/t})
5.2.7. $all 数组中的每一个元素
1 > db.bikes.find({"emails" :{$all :["1@163.com" ]}})
5.2.8. $size 匹配数组长度
1 > db.bikes.find({"emails" :{$size :2}})
5.2.9. $slice 返回部分结果
前10项
1 > db.bikes.find({"emails" :{$slice :10}})
后10项
1 > db.bikes.find({"emails" :{$slice :-10}})
跳过前23项,返回10项
1 > db.bikes.find({"emails" :{$slice :[23,10]}})
5.2.10. $eleMatch❌ 模糊查询
Error: error: {
“ok” : 0,
“errmsg” : “unknown top level operator: $elemMatch”,
“code” : 2,
“codeName” : “BadValue”
}
5.2.11. $where 查询部分
1 > db.bikes.find({$where :function (){}})
5.2.12. 游标 1 2 3 4 5 6 > var cursor=db.bikes.find() > while (cursor.hasNext()){ var obj=cursor.next(); obj.status=10; db.bikes.update({},{"status" :obj.status}) }> >db.bikes.find() { "_id" : ObjectId("5da0ac355c6be30577bc9af7"), "status" : 10 } >
在服务端,游标消耗内存和其他资源,当游标遍历尽后,数据库释放资源
当游标在客户端不在作用域内,驱动向服务器发送专门的消息,销毁游标。
当游标10分钟不使用,自动销毁
5.2.13. limit 查询2条
1 > db.bikes.find().limit (2)
5.2.14. skip 跳过前3条
1 > db.bikes.find().skip(3)
5.2.15. sort 1 升序 -1降序
1 2 > db.bikes.find().sort ({"id" :-1}) > db.bikes.find().limit ().skip().sort ()
5.2.16. count() 返回集合文档数
5.2.17. distinct 返回给定键的所有不同值
{“distinct”:”集合名”,”key”:”键名”}
1 2 3 4 5 6 7 8 9 > db.bikes.find() { "_id" : ObjectId("5da1b950873bba4fa238c6af"), "location" : [ 100, 100 ] } { "_id" : ObjectId("5da1b958873bba4fa238c6b0"), "location" : [ 110, 100 ] } { "_id" : ObjectId("5da1b95e873bba4fa238c6b1"), "location" : [ 110, 110 ] } { "_id" : ObjectId("5da1c08b873bba4fa238c6b2"), "location" : [ 90, 100 ] } > > db.runCommand({"distinct" :"bikes" ,"key" :"location" }) { "values" : [ 100, 110, 90 ], "ok" : 1 } >
5.3. 更新 db.[collectionName].update({查询器},{执行器})
更新某个键
5.3.1. $set 1 2 3 > db.bikes.update({id :0},{$set :location:[1,2]}) > db.bikes.find() { "_id" : ObjectId("5da0ac355c6be30577bc9af7"), "id" : 0, "bikeno" : 100000, "status" : 0, "location" : [ 1, 2 ] }
不加$set 导致数据丢失
1 2 3 > db.bikes.update({id :0},{location:[1,2]}) > db.bikes.find() { "_id" : ObjectId("5da0ac355c6be30577bc9af7"),"location" : [ 1, 2 ] }
5.3.2. $unset 抹去某个键,抹去bikeno
1 2 3 > db.bikes.update({id :0},{$unset :{bikeno:100000}}) > db.bikes.find() { "_id" : ObjectId("5da0ac355c6be30577bc9af8"), "id" : 0, "status" : 0, "location" : [ 1, 2 ] }
5.3.3. $inc 自增
1 > db.bikes.update({id :0},{$inc :{status:1}})
自地修改
5.3.4. $push 没有则创建,有就追加
5.3.5. $addToSet 追加
1 > db.bikes.update({"id" :0},{$addToSet :{"emails" :{$each :["1@163.com" ,"2@163.com" ,"3@163.com" ]}}})
5.3.6. $pull 删除数组中每一元素
1 > db.bikes.update({"id" :0},{$pull :{"emails" :"1@163.com" }})
5.3.7. upset true 即为updare操作,要是文档没有符合更新条件,以条件和更新文档作为基础创建一个新文档
原本没有show的键,不加true,更新操作不会增加数据
1 > db.bikes.update({"show" :0},{$inc :{"show" :1}},true )
5.3.8. save 不存在时插入,存在时更新
存在_id调用upsert,否则调用insert
5.3.9. 更新多个数据 第四个参数
1 > db.bikes.update({"show" :0},{$inc :{"show" :1}},false ,true )
5.3.10. findAndModify 等待服务器的响应
1 2 3 4 5 > db.runCommand({ ... "findAndModify":"bikes", ... "query":{"id":1}, ... "update":{$set:{"status":2}} ... })
返回更新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 { "lastErrorObject" : { "updatedExisting" : true, "n" : 1 }, "value" : { "_id" : ObjectId("5da0ac355c6be30577bc9af7"), "id" : 1, "status" : 2, "location" : [ 1, 2 ], "emails" : [ "1@163.com", "3@163.com" ], "score" : 1 }, "ok" : 1 }
findAndModify : 集合名
query 条件
update 执行器
remove true/false 删除该数据
sort 条件
5.4. 删除 删除整个集合
1 2 3 > db.bikes.remove({}) > db.bikes.find() >
删除特定
1 2 3 > db.bikes.remove({bikeno:100001}) > db.bikes.find() { "_id" : ObjectId("5da0ac355c6be30577bc9af7"), "id" : 0, "status" : 0, "location" : [ 1, 2 ] }
删除整个集合
1 2 3 > db.bikes.drop({}) > db.bikes.find() >
5.5. > help 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 > help db.help() help on db methods db.mycoll.help() help on collection methods sh.help() sharding helpers rs.help() replica set helpers help admin administrative help help connect connecting to a db help help keys key shortcuts help misc misc things to know help mr mapreduce show dbs show database names show collections show collections in current database show users show users in current database show profile show most recent system.profile entries with time >= 1ms show logs show the accessible logger names show log [name] prints out the last segment of log in memory, 'global' is default use <db_name> set current database db.foo.find() list objects in collection foo db.foo.find( { a : 1 } ) list objects in foo where a == 1 it result of the last line evaluated; use to further iterate DBQuery.shellBatchSize = x set default number of items to display on shell exit quit the mongo shell
Eg. 得到集合所在数据库
1 2 3 4 > db.getCollection("bikes" ) bike.bikes > db.getCollectionNames() ["bikes"]
6. 6种数据类型
7. _id 默认为ObjectId,12字节,每个字节两位十六进制数字,24字节字符串
0
1
2
3
4
5
6
7
8
9
10
11
时间戳
机器
PID
计数器
0-4前4位时间戳,从标准纪元开始的时间戳
5-6 3位主机唯一标识符,主机名散列值
7-8 进程唯一标识
9-11 自动增加的计数器,确保同一秒的ObjectId也不同
确保分布式时,不同主机不同进程ObjectId不同
由客户端生成,减少数据库层扩展开销
8. 请求和连接 数据库将请求连接设置成一个队列,新请求放于队尾
9. 索引 9.1. 创建索引ensureIndex 1 升序 -1降序
1 2 3 4 5 6 7 8 > db.bikes.ensureIndex({"id" :1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } >
9.2. 得数据库中所有索引getIndexes() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 > db.bikes.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "bike.bikes" } ] > db.bikes.dropIndex("_id_" ) { "nIndexesWas" : 1, "ok" : 0, "errmsg" : "cannot drop _id index", "code" : 72, "codeName" : "InvalidOptions" }
9.3. 删除索引dropIndex(“索引名”) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 > db.bikes.dropIndex("id_1" ) { "nIndexesWas" : 2, "ok" : 1 } > > db.bikes.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "bike.bikes" } ]
9.4. 删除所有索引,仍剩_id 删除所有索引,_id的索引不会被删除
1 2 3 4 5 6 7 8 9 10 db.runCommand({ ... "dropIndexes":"bikes", ... "index":"*" ... }) { "nIndexesWas" : 3, "msg" : "non-_id indexes dropped for collection", "ok" : 1 } >
9.5. 索引名”name”:”” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 > db.bikes.ensureIndex({"id" :1},{"name" :"index" }) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > > db.bikes.getIndexes() [ { "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "bike.bikes" }, { "v" : 2, "key" : { "id" : 1 }, "name" : "index", "ns" : "bike.bikes" } ] >
9.6. 唯一索引”unique”:true insert只校验_id,添加索引保存索引对应键不能重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 > db.bikes.ensureIndex({"id" :1},{"unique" :true }) { "createdCollectionAutomatically" : true, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 } > > db.bikes.insert({"id" :0,"bikeno" :10000,"status" :0}) WriteResult({ "nInserted" : 1 }) > > db.bikes.insert({"id" :0,"bikeno" :10000,"status" :0}) WriteResult({ "nInserted" : 0, "writeError" : { "code" : 11000, "errmsg" : "E11000 duplicate key error collection: bike.bikes index: id_1 dup key: { : 0.0 }" } })
9.7. 消除重复”dropDups”:true 将所有包含重复值的文档删掉,保留第一个文档
9.8. 复合索引 1 > db.bikes.ensureIndex({"id" :1,"status" :1})
9.9. 查询信息explain() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 > db.bikes.find().explain() { "queryPlanner" : { "plannerVersion" : 1, "namespace" : "bike.bikes", "indexFilterSet" : false, "parsedQuery" : { }, "winningPlan" : { "stage" : "COLLSCAN", "direction" : "forward" }, "rejectedPlans" : [ ] }, "serverInfo" : { "host" : "LearningtekiMacBook-Air.local", "port" : 27017, "version" : "3.4.23", "gitVersion" : "324017ede1dbb1c9554dd2dceb15f8da3c59d0e8" }, "ok" : 1 }
9.10. “background”:true 使操作在后台进行,仍可以进行请求处理
9.11. 地理位置索引 索引标记为2d
键必须为一对值,值范围[-180,180]
“location”:[100,100]
“location”:{“x”:100,”y”:100}
“location”:{“logtitude”:100,”latitude”:100}
1 > db.bikes.ensureIndex({"location" :[100,100]})
或者指定最大最小值
建立2000光年见方 ? ?=索引条数?
1 > db.bikes.ensureIndex({"location" :[100,100]},{"min" :-1000,"max" :1000})
9.11.1. $near $near指定的坐标由近到远,默认返回100条limit(10)指定返回10条
返回的是map集合
1 > db.bikes.find({"location" :{$near :[80,80]}})
用geoNear命令也可以实现 ??
1 2 3 4 5 6 7 8 9 10 11 > db.runCommand({ ... "geoNear":"map", ... "near":[80,80], ... "num":2 ... }) { "ok" : 0, "errmsg" : "can't find ns" } > > > db.runCommand({ "geoNear" :"map" , "near" :[80,80], "num" :2,"ns" :"bike.bikes" }) { "ok" : 0, "errmsg" : "can't find ns" } >
9.11.2. 矩形$within:{$box:[[],[]]} 指定左上角和右上角
1 > db.bikes.find({"location" :{$within :{$box :[[50,50],[105,105]]}}})
9.11.3. $within:{$center:[[],]} 指定圆心和半径
1 2 3 > db.bikes.find({"location" :{$within :{$center :[[90,90],10]}}}) { "_id" : ObjectId("5da1c08b873bba4fa238c6b2"), "location" : [ 90, 100 ] } >
10. group ns 集合
key 依据键值
initial 初始时间,所有成员都会使用
$reduce 传递的参数,当前文档,累加器文档(本组当前结果)
condition 条件,每组结果传递给客户端之前被调用一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 > db.runCommand({"group" :{ "ns":"bike.bikes", "key":day, "initial":{"time":0}, "$reduce":function(doc,prev){ if(doc.time>prev.time){ prev.price=doc.price; prev.time=doc.time; } }, “condition":{"day":{"$lt":"2010/09/30"}}, "finalize":function(prev){ } } })
11. MapReduce emit(key, value) 给MapReduce一个键值对
reduce = function(key,emits) emit返回的第一个值,相对于key的文档
this当前映射文档的引用
emit将文档某个键的计数count返回{count:1}
记录多个{count:1}文档,每一个都与集合的一个键有关,传递给reduce函数,reduce函数参数,(key,数组)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 > map=function (){ for(var key in this){ emit(key,{count:1}); } } > > reduce=function (key,emits){ total=0' for(var i in emits){ total+=emits[i].count; } return {"count",total}; } > > > reduce("x" ,[{count:1,id :1},{count:1,id :2}]) {count:2}
12. 数据库命令 db.bikes.drop() == db.runCommand({“drop”:”bikes”}) == db.$cmd.findOne({“drop”:”bikes”})
1 2 3 > db.runCommand({"drop" :"bikes" }) { "ns" : "bike.bikes", "nIndexesWas" : 2, "ok" : 1 } >
ok 执行结果 1.0/0.0
12.1. listCommands()
Eg.
buildInfo mongodb服务器版本和主机操作系统信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 db.runCommand({"buildInfo":"bike"}) { "version" : "3.4.23", "gitVersion" : "324017ede1dbb1c9554dd2dceb15f8da3c59d0e8", "modules" : [ ], "allocator" : "system", "javascriptEngine" : "mozjs", "sysInfo" : "deprecated", "versionArray" : [ 3, 4, 23, 0 ], "openssl" : { "running" : "OpenSSL 0.9.8zh 14 Jan 2016", "compiled" : "OpenSSL 0.9.8zh 14 Jan 2016" }, "buildEnvironment" : { "distmod" : "", "distarch" : "x86_64", "cc" : "gcc: Apple LLVM version 7.0.2 (clang-700.1.81)", "ccflags" : "-fno-omit-frame-pointer -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -Werror -O2 -Wno-unused-local-typedefs -Wno-unused-function -Wno-unused-private-field -Wno-deprecated-declarations -Wno-tautological-constant-out-of-range-compare -Wno-unused-const-variable -Wno-missing-braces -Wno-inconsistent-missing-override -Wno-potentially-evaluated-expression -fstack-protector-strong -mmacosx-version-min=10.8 -fno-builtin-memcmp", "cxx" : "g++: Apple LLVM version 7.0.2 (clang-700.1.81)", "cxxflags" : "-Woverloaded-virtual -stdlib=libc++ -std=c++11", "linkflags" : "-pthread -Wl,-bind_at_load -fstack-protector-strong -mmacosx-version-min=10.8 -stdlib=libc++", "target_arch" : "x86_64", "target_os" : "osx" }, "bits" : 64, "debug" : false, "maxBsonObjectSize" : 16777216, "storageEngines" : [ "devnull", "ephemeralForTest", "mmapv1", "wiredTiger" ], "ok" : 1 } >
collStats 集合信息
1 2 3 4 5 6 7 8 9 10 > db.runCommand({"collStats" :"bikes" }) { "ns" : "bike.bikes", "size" : 59, "count" : 1, "avgObjSize" : 59, "storageSize" : 4096, "capped" : false, ... }
distinct 不同值
1 > db.runCommand({"distinct" :"集合名" ,"key" :"key" ,"query" :{查询器}})
是否为主服务器
1 2 3 4 5 6 7 8 9 10 11 12 > db.runCommand({"isMaster" :1}) { "ismaster" : true, "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "maxWriteBatchSize" : 1000, "localTime" : ISODate("2019-09-11T15:18:22.161Z"), "maxWireVersion" : 5, "minWireVersion" : 0, "readOnly" : false, "ok" : 1 }
列出所有数据库,只能在admin数据库中使用
“listDatabases”:1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 > use admin switched to db admin > > db.auth("admin" ,"admin" ) 1 > db.runCommand({"listDatabases" :1}) { "databases" : [ { "name" : "admin", "sizeOnDisk" : 196608, "empty" : false }, { "name" : "bike", "sizeOnDisk" : 32768, "empty" : false }, { "name" : "local", "sizeOnDisk" : 73728, "empty" : false } ], "totalSize" : 303104, "ok" : 1 } > show dbs admin 0.000GB bike 0.000GB local 0.000GB >
集合重命名 renameCollection
1 2 3 4 5 6 7 8 9 10 11 12 13 use admin switched to db admin > > db.auth("admin" ,"admin" ) 1 > db.runCommand({"renameCollection" :"bike.bikes" , "to" :"bike.a" }) { "ok" : 1 } > use bike switched to db bike > > show tables a >
最后一次操作信息
1 2 3 4 5 6 7 8 9 db.runCommand({"getLastError":1}) { "connectionId" : 9, "n" : 0, "syncMillis" : 0, "writtenTo" : null, "err" : null, "ok" : 1 }
12.2. 固定集合 1 > db.createCollection("fixed" ,{capped:true ,size:10000,max:100});
创建固定集合,10000字节,最大容量100个文档,循环队列似淘汰最早进入的文档
13. GridFS 存储大二进制文件的机制
将大文件分成很多块,每块作为一个单独的文档存储,不产生磁盘碎片,内置在MongoDB的2GB块中
上传文件
mongofiles put /a.txt
列出所有问文件
mongofiles list
删除文件
mongofiles delete /a.txt
得到文件内容
mongofiles get /a.txt
cat /a.txt
14. db.eval 执行任意javascript脚本
system.js 数据库特有集合
db.system.js.insert({“_id”:”x”,”value”:1})
db.system.js.insert({“_id”:”y”,”value”:3})
db.eval(“return x+y;”) – 4
15. 数据库引用 1 {"$ref":"集合名","id":_id,"$db":数据库}
eg.
1 2 3 4 5 6 7 8 > db.bikes.insert({"id" :0,"text" :"test" }) > > db.bikes.insert({"_id" :1,"text" :"mongodb is fun!" ,"reference" :[{"$ref " :"bikes" ,"id" :0,"$db " :bike}}] > > var note = db.bikes.findOne("_id" :1) > note.references.forEach(function (ref){ printjson(db[ref.$ref].findOne({"_id":ref.$id})) })
16. mongodb