启动与停止
启动mongodb
- 一般情况:
mongod --dbpath=./ --port 27017
- 全网络访问:
mongod --dbpath=./ --port 27017 --bind_ip_all
停止mongodb
use admin;
db.shutdownServer();
常用命令
进入命令行模式
在终端直接输入 mongo
即可。
数据库切换
- 显示当前的数据库:
show dbs
- 切换数据库:
use <数据库名>
- 显示当前数据库中的集合:
show collections
查询
基本的查询
- 查询所有:
db.foo.find()
- 指定需要返回的键:
db.users.find({},{"username":1}); // 通过find()的第二个参数确定,这个参数中的数字为1则显示,为0则排除。如果两个都存在,则在1的基础上排除0。
查询条件
- 比较操作符
- $lt、$lte、$gt和$gte,分别对应<、<=、>、>=
- 查询18-30岁(含)的用户:
db.users.find({"age":{"$gte":18,"$lte":30}})
- 查询18-30岁(含)的用户:
- $ne表示不相等
- 查询所有名字不为joe的用户:
db.users.find({"username":{"$ne":"joe"}})
- 查询所有名字不为joe的用户:
- $lt、$lte、$gt和$gte,分别对应<、<=、>、>=
- OR查询
- $in可以用来查询一个键的多个值(可以指定不同类型的条件和值)
- 找出4个与中奖号码匹配的文档:
db.raffle.find({"ticket_no":{"$in":[725,542,390,391]}})
- 匹配user_id等于123456的文档:
db.users.find({"user_id":{"$in":[12345,"joe"]}}) // 同时也会匹配user_id等于joe的文档
- 与$in相对的是$nin,它可以返回所有不匹配的的文档。
- 找出4个与中奖号码匹配的文档:
- $or更通用一些,可以在多个键中查询任意的给定值
- 例如:
db.raffle.find({"$or":[{"ticket_no":725},{"winner":true}]})
- 例如:
db.raffle.find({"$or":[{"ticket_no":{"$in":[725,542,390]}},{"winner":true}]})
- 使用普通的AND型查询时,总是希望尽可能用最少的条件来限定结果的范围。OR型查询正相反:第一个条件应该尽可能匹配更多的文档,这样才是最为高效的。
- 例如:
- $in可以用来查询一个键的多个值(可以指定不同类型的条件和值)
- $not查找与特定模式不匹配的文档(与正则表达式联合使用极为有用)
- 例如:
db.users.find({"id_num":{"$not":{"$mod":[5,1]}}})
- 例如:
特定类型的查询
查询结果为键值null的文档
db.c.find({"z":null}) // 它会查询z的值为null的文档,同时也会查询到没有z这个键的文档`
db.c.find({"z":{"$in":[null],"$exists":true}}) // 它仅仅会查询z的值为null的文档`
正则表达式
- 例如:
db.users.find({"name":/joe?/i})
- 假如正则表达式被插入到的文档当中,也可以通过find查询到自身。(应该是被当作字符串处理了)
- 例如:
查询数组
最基本
db.food.insert({"fruit":["apple","banana","peach"]})
db.food.find({"fruit":"banana"}) // 它可以成功匹配到上面的文档
$all通过多个元素来匹配数组
假如已有三个元素的集合:
db.food.insert({"_id":1,"fruit":["apple","banana","peach"]})
db.food.insert({"_id":2,"fruit":["apple","banana","peach"]})
db.food.insert({"_id":3,"fruit":["apple","banana","peach"]})
查找既有”apple”又有”banana”的文档:
db.food.find({fruit:{$all:["apple","banana"]}}) // 这里的顺序是不重要的
精准匹配
db.food.insert({"fruit":["apple","banana","peach"]}) // 已有文档
db.food.find({"fruit":["apple","banana","peach"]}) // 可以匹配到
db.food.find({"fruit":["apple","banana"]}) // 匹配不到
db.food.find({"fruit":["banana","apple","peach"]}) // 匹配不到
通过数组下标查询
db.food.insert({"fruit":["apple","banana","peach"]}) // 已有文档
db.food.find({"fruit.2":"peach"}) // 数组下标都是从0开始的
$size可以用来查询特定长度的数组
- 例如:
db.food.find({"fruit":{"$size":3}})
- 例如:
$slice可以返回某个键匹配的数组元素的一个子集(find的第二个参数是指定需要返回的键)
db.blog.posts.findOne(criteria,{"comments":{"$slice":10}}) // 返回前10条评论
db.blog.posts.findOne(criteria,{"comments":{"$slice":-10}}) // 返回后10条评论
db.blog.posts.findOne(criteria,{"comments":{"$slice":[23,10]}}) // 跳过前23个元素,返回第24-33个元素。如果数组不够33个元素,则返回第23个元素后面的所有元素。
返回一个匹配的数组元素
db.blog.posts.find({"comments.name":bob},{"comments.$":1}) // 返回众多评论中的第一条数据,其结果依然是数组形式的。
$elemMatch要求同时使用查询条件中的多条语句与一个数组进行比较
数组和范围查询的互相作用
假如已有四个元素的集合:
{"x":5}
{"x":15}
{"x":25}
{"x":[5,25]}
查找10-20之间的数组:
db.test.find({"x":{"$gt":10,"$lt":20}}) // 错误的,它返回的是上面第2和第4个文档
db.test.find({"x":{"$eleMatch":{"$gt":10,"$lt":20}}}) // 只返回第4个文档
db.test.find({"x":{"$gt":10,"$lt":20}}).min({"x":10}).max({"x":20}) //通过索引限定范围可以只返回第3个文档
查询内嵌文档
假如
db.blog
集合中有以下数据:{"content":"…","comments":[{"author":"joe","score":3,"comment":"nice post"},{"author":"mary","score":6,"comment":"terrible post"}]}
查找joe发表的5分以上的评论
- 不能使用
db.blog.find({"comments":{"author":"joe","score":{"$gte":5}}})
来查询,因为内嵌文档的匹配,必须要整个文档完全匹配,而这个查询不会匹配”comment”键。 - 使用
db.blog.find({"comments.author":"joe","comments.score":{"$gte":5}})
也不行,因为符合author条件的评论和符合score条件的评论可能不是同一条评论。 - 正确方式:
db.blog.find({"comments":{"$elemMatch":{"author":"joe","score":{"$gte":5}}}})
- 不能使用
游标
- 排序sort
db.c.find().sort({username:1,age:-1}) // 1剩序、2倒序,如果指定了多个键,则按照这些键被指定的顺序诸葛排序。
- 限定返回结果数量limit
db.c.find().limit(3) // 返回3个结果,这里的数量是上限,所以少于3时,该显示几个结果就是几个结果
- 略过一定数量skip
db.c.find().skip(3) // 如果集合里能匹配的文档少于3个,这不会返回任何文档。
查看查询语句执行效率
- 示例:
db.c.find().explain()
- 各字段含义:
- millis:查询的执行速度,单位毫秒。
- n:实际返回的文档数量
- nscanned:反映了在执行这个查询过程中搜索了多少索引条目
- nscannedObjects:反映了在执行这个查询过程中扫描的文档数量
- scanAndOrder:MongoDB不得不在内存中对结果进行排序时会返回true
- isMultiKey:用于说明本次查询是否使用了多键索引
- indexOnly:是否只使用索引就完成了此次查询。查询的内容都是索引指定的字段,包括_id才会返回true。
- nYields:为了让写入请求能够顺利执行,本次查询暂停的次数。
- indexBounds:这个字段描述了索引的使用情况,给出了索引的遍历范围。
- cursor
- 示例:”cursor”:”BtreeCursor age_1_username_1”
- BtreeCursor表示本次查询使用了索引,具体来看使用了age和username进行了顺序排序
- 如果包含reverse表示对结果进行了逆序排列
- 如果包含multi表示使用了多键索引
插入
- 单个:
db.foo.insert({"bar":"baz"})
- 批量:
db.foo.batchInsert([{"_id":0},{"_id":1},{"_id":2}])
删除
- 删除文档所有数据:
db.foo.remove()
- 删除特定条件的数据:
db.foo.remove({"opt-out":true})
- 删除集合:
db.foo.drop()
更新
- 文档替换(全部替换)
db.users.update({"name":"joe"},jsonObj); // 用jsonObj的数据一股脑的替换掉原有数据
- 因为搜索出来的可能是多条数据,且_id也会替换进去,很容易出现重复_id的错误,所以尽量使用_id作为查询条件去替换。
- 修改器替换(局部更新)
- 修改器是种特殊的键,用来指定复杂的更新操作,比如修改、增加或者删除,还可能是操作数组或者内嵌文档。
- 常见修改器
- $set:用来指定一个字段的值。如果这个字段不存在,则创建它。
db.users.update({"_id":ObjectId("46849848")},{"$set":{"name":"chenhao"}})
- $unset:删除指定的字段
db.users.update({"name":"chenhao"},{"$unset":{"age":1}}); //后面指定的值不影响$unset表达式执行
- $inc:增加or减少已有值。如果这个字段不存在,则创建它。(只对数字类型的字段有效)
db.analytics.update({"url":"www.xjchenhao.cn"},{"$inc":{"pageviews":1}}) // 后面的数字是控制增加or减少的大小
- $set:用来指定一个字段的值。如果这个字段不存在,则创建它。
- 数组修改器
- $push:向已有的数组末尾加入一个值,要是没有就创建一个新的数组。
db.blog.posts.update({"title":"i am title"},{"$push":{"comments":{"content":"i am content"}}})
- 插入多个值:
db.stock.ticker.update({"_id":"Goog"},{"$push":{"hourly":{"$each":[1,2,3,4]}}})
- 保留最后加入的几个值:
db.movies.update({"name":"chenhao"},{"$push":{"top10":{"$each":[1,2,3],"$slice":-10}}}) // 如果数组长度大于10则保留最新10个,如果小于则全部保留(注意是负数)
- 排序后保留最后的几个值:
db.movies.update({"name":"chenhao"},{"$push":{"top10":{"$each":[1,9,3,4],"$slice":-2,"$sort":{"top10":-1}}}})
- $addToSet:类似$push,但是它可以确保数组内的元素不会重复。
- $pop:从数组任意一端删除值
db.lists.update({},{"$pop":{"key":3}}); // 正数时从末尾开始删除,负数时从头部开始删除
- $pull:根据条件删除数组元素
db.lists.update({},{{$pull:{"todo":"数组元素"}}})
- 删除指定位置的数组元素:
- 可以通过指定数组的下标:
db.blog.update({"post":1},{"$inc":{"comments.0.votes":1}})
- 可以通过$修改不确定下标的元素:
db.blog.update({"comments.author":"john"},{"$set":{"comments.$.author":"jim"}})
- 可以通过指定数组的下标:
- $push:向已有的数组末尾加入一个值,要是没有就创建一个新的数组。
- 查看最后一次操作的相关信息:
db.runCommand({getLastError:1})
导入导出
- 导出:
mongodump -h dbhost -d dbname -o dbdirectory
- -h:MongDB所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:27017
- -d:需要备份的数据库实例,例如:test
- -o:备份的数据存放位置,例如:c:\data\dump,当然该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个test目录,这个目录里面存放该数据库实例的备份数据。
- 导入:
mongorestore -h dbhost -d dbname --directoryperdb dbdirectory
- -h:MongoDB所在服务器地址
- -d:需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2
- —directoryperdb:备份数据所在位置,例如:c:\data\dump\test,这里为什么要多加一个test,而不是备份时候的dump,读者自己查看提示吧!
- —drop:恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!
更新的传参和返回值
model.update(conditions, doc, option, function (err, res){})
// conditions-查询条件 doc-需要更新的数据
*** 其中res返回 {n:2, nModified:2, ok: 1} ***
*** n为匹配到的条数 nModified修改的条数 ok 表为是否成功 ***
const option = { // option选项及其默认值
safe: true, // 安全模式
upsert: false, //如果不存在则创建新纪录
multi: false, // 是否更新多个查询记录
runValidators: null, // 如果值为true,执行Validation验证。
setDefaultsOnInsert: null, // 如果upsert选项为true,在新建时插入文档定义的默认值。
strict: null, // 用严格模式跟新
overwrite: false // 禁用update-only模式,允许覆盖记录。
}
表结构修改
修改字段名
db.columnsample.updateMany(
{},
{
"$rename":{"oldFieldName":"newFieldName"}
}
)
字段类型
- NumberInt:整数数字
索引管理
新增
新的
db.collection.ensureIndex(keys, options)
旧的
db.collection.createIndex(keys, options)
注意:1.8版本之前创建索引使用createIndex(),1.8版本之后已移除该方法
示例
db.addresses.ensureIndex( { "xmpp_id": 1 }, { sparse: true } )
这个示例,那些不包含xmpp_id的键(列)的文档将不会被索引
查看
当前集合的索引
db.collection.getIndexes()
查看数据库中所有索引
db.system.indexes.find()
删除
删除指定的索引
db.collection.dropIndex("INDEX-NAME")
删除所有索引
db.collection.dropIndexes()
如果发现MongoDB使用的索引与自己希望它使用的索引不一致,可以使用hint()强制MongoDB使用特定的索引。
- 强制使用索引:
db.c.find({"age":1,"username":/.*/}).hint({"username":1,"age":1})
- 强制使用全表查询:
db.c.find({"age":1,"username":/.*/}).hint({"$natural":1})
MAC的config
- 文件路径:
/usr/local/etc/mongod.conf
- 语法格式:YAML
- 相关文章:
通过设置.mongorc.js
文件设置初始化脚本
- mac上的目录地址
~/.mongorc.js
- 启动shell时指定
--norc
参数,就可以禁止加载.mongorc.js
代码示例
设置防止删除数据的操作。
var no=()=>{print(‘Not on my watch.’)};
db.dropDatabase=DB.prototype.dropDatabase=no;
DBCollection.prototype.drop=no;
DBCollection.prototype.dropIndex=no;
获得最后一次操作完成的时间
prompt=function(){
return (new Date())+'> ';
}
显示当前使用的数据库
prompt = function () {
if (typeof db === 'undefined') {
return '(nodb)> ';
}
try {
db.runCommand({ getLastError: 1 });
}
catch (e) {
print(e);
}
return db + '> ';
}
注:3.2版本后停止使用HTTP interface,无法使用28017端口查看数据库的管理信息了。
_