MongoDB快速上手指南:数据库操作常用命令CRUD
2.1 数据库操作
默认保留的数据库
- admin: 从权限角度考虑, 这是
root
数据库, 如果将一个用户添加到这个数据库, 这个用户自动继承所有数据库的权限, 一些特定的服务器端命令也只能从这个数据库运行, 比如列出所有的数据库或者关闭服务器 - local: 数据永远不会被复制, 可以用来存储限于本地的单台服务器的集合 (部署集群, 分片等)
- config: Mongo 用于分片设置时,
config
数据库在内部使用, 用来保存分片的相关信息> show dbs admin 0.000GB config 0.000GB local 0.000GB > use articledb switched to db articledb > show dbs admin 0.000GB config 0.000GB local 0.000GB 复制代码
当我们创建了一个数据库后再进行查看会发现,我们创建的数据库并没有显示出来,这是由于MongoDD的存储机制决定的
当使用
use articledb
的时候.articledb
其实存放在内存之中, 当articledb
中存在一个 collection 之后, mongo 才会将这个数据库持久化到硬盘之中.
javascript copyable" lang="JavaScript">> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> use articledb
switched to db articledb
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> db.articledb.insertOne({"a": 3})
{
"acknowledged" : true,
"insertedId" : ObjectId("62e128b6a70e7344a5139207")
}
> show dbs
admin 0.000GB
articledb 0.000GB
config 0.000GB
local 0.000GB
复制代码
另外: 数据库名可以是满足以下条件的任意UTF-8字符串。
- 不能是空字符串("")。
- 不得含有
' '
空格)、.
、$
、/
、和
0
(空字符)。 - 应全部小写。
- 最多64字节。
集合操作与数据库操作类似,这里不再单独演示
2.2 文档基本 CRUD
官方文档: docs.mongodb.com/manual/crud…
2.2.1 创建 Create
Create or insert operations add new documents to a collection. If the collection does not currently exist, insert operations will create the collection automatically.
文档的数据结构和 JSON 基本一样。
所有存储在集合中的数据都是 BSON 格式。
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称
- 使用
db.
向集合中添加一个文档, 参数一个 json 格式的文档 -db.collection.insertOne() 用于向集合插入一个新文档,语法格式如下:.insertOne() db.collection.insertOne(
, { writeConcern: } ) 复制代码 - 使用
db.
向集合中添加多个文档, 参数为 json 文档数组 db.collection.insertMany() 用于向集合插入一个多个文档,语法格式如下:.insertMany()
db.collection.insertMany(
[ 1> , 2>, ... ],
{
writeConcern: ,
ordered: <boolean>
}
)
复制代码
参数说明:
- document:要写入的文档。
- writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
- ordered:指定是否按顺序写入,默认 true,按顺序写入
我们平时使用最多的只有
document
这一个字段
# 插入单条数据
> var document = db.collection.insertOne({"a": 3})
> document
{
"acknowledged" : true,
"insertedId" : ObjectId("571a218011a82a1d94c02333")
}
# 插入多条数据
> var res = db.collection.insertMany([{"b": 3}, {'c': 4}])
> res
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("571a22a911a82a1d94c02337"),
ObjectId("571a22a911a82a1d94c02338")
]
}
复制代码
还可以通过js函数方式批量插入文档:
1、先创建数组 2、将数据放在数组中 3、一次 insert 到集合中
var arr = [];
for(var i=1 ; i<=20000 ; i++){
arr.push({num:i});
}
db.numbers.insert(arr);
复制代码
注:当我们向 collection
中插入 document
文档时, 如果没有给文档指定 _id
属性, 那么数据库会为文档自动添加 _id
field, 并且值类型是 ObjectId(blablabla)
, 就是文档的唯一标识, 类似于 relational database 里的 primary key
- mongo 中的数字, 默认情况下是 double 类型, 如果要存整型, 必须使用函数
NumberInt(整型数字)
, 否则取出来就有问题了- 插入当前日期可以使用
new Date()
如果某条数据插入失败, 将会终止插入, 但已经插入成功的数据不会回滚掉. 因为批量插入由于数据较多容易出现失败, 因此, 可以使用 try catch
进行异常捕捉处理, 测试的时候可以不处理.如:
try {
// 插入多条记录
db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
} catch (e) {
print (e);
}
复制代码
2.2.2 查询 Read
更多查询可以看2.4节和2.5节
- 使用
db.
方法对集合进行查询, 接受一个 json 格式的查询条件. 返回的是一个数组.find() db.
查询集合中符合条件的第一个文档, 返回的是一个对象.findOne()
// 插入多条记录
> db.comment.insertMany([
{"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
{"_id":"2","articleid":"100001","content":"我夏天空腹喝凉开水,冬天喝温开水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
{"_id":"3","articleid":"100001","content":"我一直喝凉开水,冬天夏天都喝。","userid":"1004","nickname":"杰克船长","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
{"_id":"4","articleid":"100001","content":"专家说不能空腹吃饭,影响健康。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
{"_id":"5","articleid":"100001","content":"研究表明,刚烧开的水千万不能喝,因为烫嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08-06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
{
"acknowledged" : true,
"insertedIds" : [
"1",
"2",
"3",
"4",
"5"
]
}
// 只返回查询到的第一条数据
> db.comment.findOne({"articleid":"100001"})
{
"_id" : "1",
"articleid" : "100001",
"content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我他。",
"userid" : "1002",
"nickname" : "相忘于江湖",
"createdatetime" : ISODate("2019-08-05T22:08:15.522Z"),
"likenum" : 1000,
"state" : "1"
}
// 等价于
db.comment.find({"articleid":"100001"}).limit(1)
复制代码
如果我们不需要那么多的字段,我们可以在查询条件后面再跟上需要查询的字段,1
表示显示指定的字段,其中_id
是默认显示的,我们指定0
表示强制不显示
// 只显示articleid字段
> db.comment.find({"articleid":"100001"},{"articleid":1}).limit(1)
{ "_id" : "1", "articleid" : "100001" }
// 强制_id不显示
> db.comment.find({"articleid":"100001"},{"articleid":1,"_id":0}).limit(1)
{ "articleid" : "100001" }
复制代码
可以使用 $in
操作符表示范围查询
db.inventory.find( { status: { $in: [ "A", "D" ] } } )
复制代码
多个查询条件用逗号分隔, 表示 AND
的关系
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
复制代码
等价于下面 sql 语句
SELECT * FROM inventory WHERE status = "A" AND qty < 30
复制代码
使用 $or
操作符表示后边数组中的条件是OR的关系
db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
复制代码
等价于下面 sql 语句
SELECT * FROM inventory WHERE status = "A" OR qty < 30
复制代码
联合使用 AND
和 OR
的查询语句
db.inventory.find( {
status: "A",
$or: [ { qty: { $lt: 30 } }, { item: /^p/ } ]
} )
复制代码
在 terminal 中查看结果可能不是很方便, 所以我们可以用 pretty()
来帮助阅读
db.inventory.find().pretty()
复制代码
匹配内容
db.posts.find({
comments: {
$elemMatch: {
user: 'Harry Potter'
}
}
}).pretty()
// 正则表达式
db..find({ content : /once/ })
复制代码
创建索引
db.posts.createIndex({
{ title : 'text' }
})
// 文本搜索
// will return document with title "Post One"
// if there is no more posts created
db.posts.find({
$text : {
$search : ""Post O""
}
}).pretty()
复制代码
2.2.3 更新 Update
- 使用
db.
方法修改一个匹配.updateOne( , , )
条件的文档 - 使用
db.
方法修改所有匹配.updateMany( , , )
条件的文档 - 使用
db.
方法替换一个匹配.replaceOne( , , )
条件的文档 db.
默认情况下会使用新对象替换旧对象.update(查询对象, 新对象)
其中
参数与查询方法中的条件参数用法一致
覆盖修改,会将其他的值清除
// nModified1表示有一条记录被修改
> db.comment.update({"_id":"1"}, {"likenum":NumberInt(1001)})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
// 可以看到其他字段的值不见了
> db.comment.find()
{ "_id" : "1", "likenum" : 1001 }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T11:01:02.521Z"), "likenum" : 3000, "state" : "1" }
复制代码
局部修改,只修改我们修改的部分,其他字段不受影响
如果需要修改指定的属性, 而不是替换需要用“修改操作符”来进行修改
$set
修改文档中的制定属性
// 发现局部修改后其他字段并不受影响
> db.comment.update({ "_id": "2" }, {$set:{ "likenum": NumberInt(1001) }})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.comment.find()
{ "_id" : "1", "likenum" : 1001 }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 1001, "state" : "1" }
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("2019-08-06T11:01:02.521Z"), "likenum" : 3000, "state" : "1" }
复制代码
其中最常用的修改操作符即为$set
和$unset
,分别表示赋值和取消赋值.
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
db.inventory.updateMany(
{ qty: { $lt: 50 } },
{
$set: { "size.uom": "in", status: "P" },
$currentDate: { lastModified: true }
}
)
复制代码
- uses the
$set
operator to update the value of thesize.uom
field to"cm"
and the value of thestatus
field to"P"
,- uses the
$currentDate
operator to update the value of thelastModified
field to the current date. IflastModified
field does not exist,$currentDate
will create the field. See$currentDate
for details.
db.
方法替换除 _id
属性外的所有属性, 其
参数应为一个全新的文档.
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)
复制代码
批量修改
在后面添加{multi: true}
即可
// 默认会修改第一条
db.commnet.update({ userid: "30", { $set {username: "guest"} } })
// 修改所有符合条件的数据
db.commnet.update( { userid: "30", { $set {username: "guest"} } }, {multi: true} )
复制代码
列值增长的修改
如果我们想实现对某列值在原有值的基础上进行增加或减少, 可以使用 $inc
运算符来实现
db.commnet.update({ _id: "3", {$inc: {likeNum: NumberInt(1)}} })
复制代码
修改操作符
2.2.4 删除 Delete
db.collection.remove()
通过添加删除规则进行删除- 使用
db.collection.deleteMany()
方法删除所有匹配的文档. - 使用
db.collection.deleteOne()
方法删除单个匹配的文档. db.collection.drop()
db.dropDatabase()
只删除一条记录
如果不加后面的限制会删除所有匹配的记录
以下语句可以将数据全部删除,请慎用
db.comment.remove({})
复制代码
Delete operations do not drop indexes, even if deleting all documents from a collection.
一般数据库中的数据都不会真正意义上的删除, 会添加一个字段, 用来表示这个数据是否被删除
2.3 文档排序和投影 (sort & projection)
2.3.1 排序 Sort
在查询文档内容的时候, 默认是按照 _id
进行排序
我们可以用 $sort
更改文档排序规则
{ $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
复制代码
For the field or fields to sort by, set the sort order to 1
or -1
to specify an ascending or descending sort respectively, as in the following example:
db.users.aggregate(
[
{ $sort : { age : -1, posts: 1 } }
// ascending on posts and descending on age
]
)
复制代码
$sort
Operator and Memory
$sort
+ $limit
Memory Optimization
When a $sort
precedes a $limit
and there are no intervening stages that modify the number of documents, the optimizer can coalesce the $limit
into the $sort
. This allows the $sort
operation to only maintain the top n
results as it progresses, where n
is the specified limit, and ensures that MongoDB only needs to store n
items in memory. This optimization still applies when allowDiskUse
is true
and the n
items exceed the aggregation memory limit.
Optimizations are subject to change between releases.
有点类似于用 heap 做 topK 这种问题, 只维护 k 个大小的 heap, 会加速 process
举个栗子:
db.posts.find().sort({ title : -1 }).limit(2).pretty()
复制代码
2.3.2 投影 Projection
有些情况, 我们对文档进行查询并不是需要所有的字段, 比如只需要 id 或者 用户名, 我们可以对文档进行“投影”
1
- display0
- dont display
> db.users.find( {}, {username: 1} )
> db.users.find( {}, {age: 1, _id: 0} )
复制代码
2.4 分页查询
2.4.1 统计查询
统计查询使用count()方法,语法如下:
db.collection.count(query, options)
复制代码
参数:
提示: 可选项暂时不使用。
【示例】
(1)统计所有记录数: 统计comment集合的所有的记录数:
(2)按条件统计记录数:例如:统计userid为1003的记录条数
提示: 默认情况下 count() 方法返回符合条件的全部记录条数。
2.4.2 分页列表查询
可以使用limit()方法来读取指定数量的数据,使用skip()方法来跳过指定数量的数据
基本语法如下所示:
db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
复制代码
2.4.3 排序查询
sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用 于降序排列。
语法如下所示:
db.COLLECTION_NAME.find().sort({KEY:1})
或
db.集合名称.find().sort(排序方式)
复制代码
例如: 对userid降序排列,并对访问量进行升序排列
提示: skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是先 sort(), 然后是 skip(),最后是显示的 limit(),和命令编写顺序无关。
2.5 其他查询方式
2.5.1 正则表达式(模糊查询)
MongoDB的模糊查询是通过正则表达式的方式实现的。格式为
$ db.collection.find({field:/正则表达式/})
$ db.collection.find({字段:/正则表达式/})
复制代码
提示:正则表达式是js的语法,直接量的写法。 例如,我要查询评论内容包含“开水”的所有文档,代码如下:
如果要查询评论的内容中以“专家”开头的,代码如下:
附录:常用的正则表达式
2.5.2 比较查询
<
, <=
, >
, >=
这些操作符也是很常用的, 格式如下:
其实这些字符就是对应JS里面的:gt(great than)、lt(less than)、gte(great than equal )、lte(less than equal )、ne(not equal)
db.collection.find({ "field" : { $gt: value }}) // 大于: field > value
db.collection.find({ "field" : { $lt: value }}) // 小于: field < value
db.collection.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.collection.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.collection.find({ "field" : { $ne: value }}) // 不等于: field != value
复制代码
示例:查询评论点赞数量大于700的记录
2.5.3 包含查询
包含使用 $in
操作符. 示例:查询评论的集合中 userid
字段包含 1003
或 1004
的文档
db.comment.find({userid:{$in:["1003","1004"]}})
复制代码
不包含使用 $nin
操作符. 示例:查询评论集合中 userid
字段不包含 1003
和 1004
的文档
db.comment.find({userid:{$nin:["1003","1004"]}})
复制代码
2.5.4 条件连接查询
我们如果需要查询同时满足两个以上条件,需要使用$and操作符将条件进行关联。(相当于SQL的and) 格式为:
$and:[ { },{ },{ } ]
复制代码
示例:查询评论集合中likenum大于等于700 并且小于2000的文档:
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
复制代码
如果两个以上条件之间是或者的关系,我们使用 操作符进行关联,与前面 and的使用方式相同 格式为:
$or:[ { },{ },{ } ]
复制代码
示例:查询评论集合中userid为1003,或者点赞数小于1000的文档记录
db.comment.find({$or:[ {userid:"1003"} ,{likenum:{$lt:1000} }]})
复制代码
2.5.5 foreach查询
我们知道这些查询语句其实就是js
的语法格式,所有在查询得到结果后我们也可以通过forEach
函数对结果进行遍历
db.posts.find().forEach(
fucntion(doc) {
print('Blog Post: ' + doc.title)
})
// 也可以通过箭头函数简化一下
db.comment.find().forEach((it)=> {
print(it._id)
});
复制代码
2.5.6 地理位置查询
请查看MongoDB中文文档:地理空间查询 - MongoDB-CN-Manual (mongoing.com)
2.6 常用命令小结
选择切换数据库:use articledb
插入数据:db.comment.insert({bson数据})
查询所有数据:db.comment.find();
条件查询数据:db.comment.find({条件})
查询符合条件的第一条记录:db.comment.findOne({条件})
查询符合条件的前几条记录:db.comment.find({条件}).limit(条数)
查询符合条件的跳过的记录:db.comment.find({条件}).skip(条数)
修改数据:db.comment.update({条件},{修改后的数据})
或
db.comment.update({条件},{$set:{要修改部分的字段:数据})
修改数据并自增某字段值:db.comment.update({条件},{$inc:{自增的字段:步进值}})
删除数据:db.comment.remove({条件})
统计查询:db.comment.count({条件})
模糊查询:db.comment.find({字段名:/正则表达式/})
条件比较运算:db.comment.find({字段名:{$gt:值}})
包含查询:db.comment.find({字段名:{$in:[值1, 值2]}})
或
db.comment.find({字段名:{$nin:[值1, 值2]}})
条件连接查询:db.comment.find({$and:[{条件1},{条件2}]})
或
db.comment.find({$or:[{条件1},{条件2}]})
复制代码
3. 文档间的对应关系
- 一对一 (One To One)
- 一对多/多对一(one to many / many to one)
- 多对多 (Many To Many)
作者:是小梁同学呀
来源:稀土掘金
本站所有文章和图片均来自用户分享和网络收集,文章和图片版权归原作者及原出处所有,仅供学习与参考,请勿用于商业用途,如果损害了您的权利,请联系网站客服处理。