MongoDB 学习二

这章我们学习数据操作。

 

Inserting and Saving Documents

上一章我们已经简单介绍了数据插入,如:

db.foo.insert({"bar":"baz"})

那么,假如你碰到需要插入多条documents的时候情况怎么办呢?

只要传入数组就行

db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])

插入验证

MongoDB在插入数据的时候仅做最少的检查。它检查document的基础结构并且"_id"不存在时帮你自动添加。

一种数据结构的检查是所有的document都必须小于16M。

 

Removing Documents

假如我们数据库中有数据,让我们来删除它:

db.foo.remove()

这是我们会删除foo中所有的documents。实际上它并不会删除foo这个collection。

remove这个方法支持一个查询document的参数,当我们传入这个参数时,只有匹配这个查询参数的document才会被删除。

当我们执行remove时,它就永久被删除,没有任何方法可以恢复。

 

Remove Speed

删除文档是一个相当快的操作,但是你想要清除整个collection,最快的是drop方法

 

Updating Documents

当document存储在数据库中时,我们可以使用update方法更改它。

update有两个参数:一个用来定位我们需要更新的document,另一个是我们需要修改的document。

更新document是原子性的:假如有两个update动作发生在同一时间,哪个操作先到达服务器端将会先接收,第二个操作会后接收。

 

Using Modifiers

通常我们只要更新document中的某些部分。你可以使用update modifiers来更新特殊字段。 Update Modifiers是用于指定复杂更新操作的特殊key值,例如altering,adding,或者removing keys。

假设我们要分析网站访问量,我们可以使用update modifiers来做这个自动增量,例如原来的数据:

{
   "_id":ObjectId("..."),
   "url": "www.example.com",
   "pageviews":1
}

每次访问页面,我们可以根据它的url查找并且使用"$inc" modifier 来增加pageviews的值

db.foo.update({"url":"www.example.com"},{"$inc":{"pageviews":1}})

 

Getting started with the "$set" modifier

 "$set"可以设置字段值。假如字段不存在,它会被创建。

 举个例子,假设你有个用户简况存在数据库中:

{
   "_id":ObjectId("4XXXX"),
   "name":"joe",
   "age":30,
   "sex":"male",
   "location":"Wisconsin"
}

这是最基本的信息,假如用户想要存储他最喜欢的书,你可以使用"$set":

db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":"War and Peace"}})

"$set"甚至可以更改它的类型,假如用户实际上喜欢多本书,我们可以将favorite book这个值存入数组:

db.users.update({"_id":ObjectId("4XXXX")},{"$set":{"favorite book":["War and Peace","Cat's Cradle"]}})

当用户发现他实际上不喜欢阅读时,他也可以使用"$unset"来移除key

db.users.update({"_id":ObjectId("4XXXX")},{"$unset":{"favorite book":1}})

 

Array modifers

 "$push"可以添加数据到类型为Array的字段。假如字段不存在,它会被创建。

例如原有数据:

{
    "_id":ObjectId("4XXX"),
    "title":"A blog post"
}

添加cmments的数组字段:

db.blog.update({"title":"A blog post"},{"$push":{"comments":{"name":"xc","content":"nice post"}}})

再来看下数据,这时变为:

{
    "_id":ObjectId("4XXX"),
    "title":"A blog post",
    "comments":[
          {
               "name":"xc",
               "content":"nice post"
           }
     ]
}

 这只是简单的对数组进行push,可是我们要插入多条数组数据怎么做呢?答案是使用"$each":

db.blog.update({"title":"A blog post"},{"$push":{"comments":{"$each":[{"name":"xc","content":"nice post"},{"name":"cc","content":"test"}]}}})

 

Using arrays as sets

如果你只想添加数组中没有的元素,可以使用"$ne":

db.papers.update({"authors":{"$ne":"Richie"}},{"$push":{"authors":"Richie"}})

你也可以通过"$addToSet"完成这个工作:

db.blog.update({"title":"A blog post"},{"$addToSet":{"comments":{"name":"xc","content":"nice post"}}})

 

Removing elements

移除数组最后一个元素,用

{"$pop":{"key":1}}

移除数组第一个元素,用

{"$pop":{"key":-1}}

根据条件匹配删除数据,用"$pull",例:

db.list.update({},{"$pull":{"todo":"laundry"}})

 

Positional array modifications

当我们在一个数组中有多个值并且想要修改它们时,数组操作变的很机智。

我们可以基于数组的索引来选取它们。

 {
 "_id" : ObjectId("55ef9fdcb01a89febec546be"),
"comments": [ {"comment":"x","author":"John","votes":0},
{"comment":"y","author":"Claire","votes":3},
{"comment":"z","author":"Alice","votes":-1} ] }

我们想要使第一条comments的votes加1,我们可以这样做(comments.0.votes代表第一条comments的votes字段):

db.foo.update({},{"$inc":{"comments.0.votes":1}})

在许多情况下,我们不知数组的索引来查找并修改数组。结果MongoDB出现了一个定位操作,"$",可以指出数组中匹配的元素并进行跟新。

例如,我们有个用户交John,并且我们想把它的名字变为Jim,我们就可以这样写:

db.foo.update({"comments.author":"John"},{"$set":{"comments.$.author":"Jim"}})

 

Upserts

upsert是更新的一种特殊类型(其实就是update的第三个参数)。假如我们通过更新匹配条件没有找到document,它会结合条件创建并更新document。假如找到了,它则正常更新。

举个例子:

//check if we have an entry for this page
blog = db.analytics.findOne({url:"/blog"})

//if we do,add one to the number of views and save
if(blog){
    blog.pageviews++;
    db.analytics.save(blog);
}

//otherwise,create a new document for this page
else{
    db.analytics.save({url:"/blog",pageviews:1})
}

我们用upsert改下

db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)

upsert如果不加或者是false。如果查不到匹配的document时则什么都不会发生。

在upsert的基础上,有些时间我们只需要在创建document的时候对某个字段赋值,并不想更新它的值,可以用"$setOnInsert":

 db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)

我们可以看到数据为

{ "_id" : ObjectId("55efc4a3de70c83a19f2d9b9"), "createdAt" : ISODate("2015-09-09T05:34:07.791Z") }

这时候继续在打命令

 db.users.update({},{"$setOnInsert":{"createdAt":new Date()}},true)

我们发现createAt字段的值还是没有变化。

 

Updating Multiple Documents

update方法默认的只会根据匹配条件更新第一条数据。假如有更多的匹配出的document,则不会更新。

如果需要全部更新,你只要指定第四个参数为true就可以了。

例如我们想为所有生日在某一天的用户添加一个gift字段,可以这样写:

db.users.update({"birthday":"10/13/1978"},{"$set":{"gift":"Happy Birthday!"}},false,true);

 

Returning Updated Documents

我们执行update操作时,不会返回更新的数据,这时,你需要findAndModify命令。并且它是原子性的。

假设我们有些订单需要跑,数据结构为

{
    "id":ObjectId(),
    "status":state,
    "priority":N
}

"status"可以是"READY","RUNNING"或者"DONE"。我们有个任务,需要根据"READY"的优先级顺序,跑process方法,并且更新其状态为"DONE"。

我们需要根据排序优先级查找出document准备跑porcess方法,状态标记为"RUNNING",当我们跑完方法时,更新状态为"DONE",看上去像是这样:

var cursor = db.processes.find({"status":"READY"});
ps = cursor.sort({"priority":-1}).limit(1).next();
db.processes.update({"_id":ps._id},{"$set":{"status":"RUNNING"}});
do_something(ps);
db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});

这种算法并不好。假设我们有两个线程在跑。假如线程一和线程二同时检索到同一条数据,那么将跑同样的process。

我们可以做状态检查来避免这种情况,但是这将会变的复杂:

var cursor = db.processes.find({"status":"READY"});
cursor.sort({"priority":-1}).limit(1);
while((ps=cursor.next())!=null){
    ps.update({"_id":ps._id,"status":"READY"},{"$set":{"status":"RUNNING"}});
    var lastOp = db.runCommand({getlasterror:1});
    if(lastOp.n == 1){
        do_something(ps);
        db.processes.update({"_id":ps._id},{"$set":{"status":"DONE"}});
        break;
    }
    cursor = db.process.find({"status":"READY"});
    cursor.sort({"priority":-1}).limit(1);
}

另外,一个线程可能会做完所有的工作,而另外一个线程一直处于等待失败的状态中。

类似这种情况,"findAndModify"完美解决。"findAndModify"能够在一个操作中同时返回并更新数据。

ps = db.runCommand(
      {
         "findAndModify":"processes",
         "query":{"status":"READY"},
         "sort":{"priority":-1},
         "update":{"$set":{"status":"RUNNING"}}
      }
)

返回值:

{
     "ok":1,
     "value":{
            "_id":ObjectId("4XXXX"),
            "priority":1,
            "status":"READY"
     }
}

这里返回的状态是READY,为更新前的状态数据。假如你这时查找数据库,会发现状态已经更新了。

 

"findAndModify"除了有"udpate"这个key,也有"remove"这个key。 想必这个不用解释了吧。

ps = db.runCommand(
      {
         "findAndModify":"processes",
         "query":{"status":"READY"},
         "sort":{"priority":-1},
         "remove":true}
      }
)

 

这章介绍了MongoDB的创建、删除、修改操作,下章将介绍查询操作,敬请期待...

 

转载于:https://www.cnblogs.com/showtime813/p/4789038.html

<think> 嗯,用户明确表示需要MongoDB学习资料和方法,看来ta正在入门或者想系统学习这个数据库。从用户提问的简洁程度看,可能是个新手,不太清楚具体需要哪些资源,需要引导。 之前提供的引用材料挺全的,但需要整理得更友好。引用1提到MongoDB有基础操作、系统管理等完整学习路径;引用2给出了具体的学习资料列表,包括入门到进阶的11个主题;引用3则提供了SQL到MongoDB的语法对照链接——这些正好能组成体系化学习方案。 用户可能更关注如何快速上手,所以要把资料按学习阶段分层。第一阶段应该优先文档基本操作,特别是SQL用户最关心的CRUD语法转换(这里引用3的SQL对照表特别有用);第阶段再深入索引、聚合这些进阶内容;最后才是复制分片等运维知识。这样阶梯式推进比较符合认知规律。 考虑到中文用户的需求,特意补充了中文教程资源。官方文档虽然权威但英文可能造成障碍,所以把中文社区和视频教程放在显眼位置。性能优化和模式设计这类实战内容单独作为第四阶段,因为用户实际使用时最容易卡在这些地方。 最后用思维导图总结学习路径是点睛之笔。用户可能面临信息过载,用图形化方式呈现“基础→核心→高级→实战”四个阶段,能帮ta建立全局认知。每个阶段的关键词(集合/索引/聚合等)都来自引用材料里的核心概念,确保准确性。 要不要提醒用户先装环境呢?不过资料推荐里已经包含官方快速入门,应该足够。如果用户后续追问安装问题再补充也不迟。 </think>根据您的需求,以下是系统化的MongoDB学习路径和精选资源,结合官方文档实战教程: --- ### **一、分阶段学习路径** #### **第一阶段:基础概念** 1. **核心概念** - **文档(Document)**:类JSON的BSON格式数据单元(例:`{ name: "Alice", age: 25 }`) - **集合(Collection)**:文档的容器(类似SQL表) - **数据库(Database)**:集合的物理分组 > 参考官方文档:[MongoDB Glossary](https://www.mongodb.com/docs/manual/reference/glossary/) [^1] 2. **数据类型** 对比SQLMongoDB数据类型映射: | SQL | MongoDB | 示例 | |-----------|------------|-----------------------| | INT | int32 | `age: 25` | | VARCHAR | string | `name: "Alice"` | | DATETIME | Date | `created: ISODate()` | #### **第阶段:核心操作** 1. **CRUD操作**([SQL对比指南](https://www.mongodb.com/docs/manual/reference/sql-comparison/) [^3]) ```javascript // 插入文档 db.users.insertOne({name: "Bob", role: "admin"}) // 查询带条件 db.users.find({age: {$gt: 20}}) // 更新字段 db.users.updateOne({name: "Bob"}, {$set: {status: "active"}}) ``` 2. **聚合框架**([SQL聚合对比](https://www.mongodb.com/docs/manual/reference/sql-aggregation-comparison/) [^3]) ```javascript // 统计各角色用户数 (类比SQL: SELECT role, COUNT(*) FROM users GROUP BY role) db.users.aggregate([ { $group: { _id: "$role", count: { $sum: 1 } } } ]) ``` #### **第三阶段:进阶优化** 1. **索引优化** - 创建索引:`db.users.createIndex({email: 1})` - 索引类型:单字段、复合、文本、地理空间等 > 性能影响:[索引策略指南](https://www.mongodb.com/docs/manual/applications/indexes/) [^1] 2. **模式设计** - 嵌入式文档 vs 引用式关联 - 分桶模式(Bucket Pattern)处理时序数据 #### **第四阶段:高可用扩展** 1. **复制集(Replica Set)** - 自动故障转移:1个Primary + N个Secondary - 数据冗余:异步复制 2. **分片(Sharding)** - 水平扩展:按分片键(如`user_id`)分布数据 - 集群组件:Mongos、Config Server、Shard --- ### **、精选学习资源** 1. **官方文档** - [MongoDB中文手册](https://www.mongodb.com/docs/manual/):最权威的入门到进阶指南 [^1] - 交互式教程:[MongoDB University](https://learn.mongodb.com/)(免费课程) 2. **中文实战教程** - [《MongoDB实战精解》](https://item.jd.com/12693972.html):模式设计+性能优化案例 - 视频课程:[B站MongoDB大师课](https://www.bilibili.com/video/BV18f4y1R7pk)(索引集群配置演示) 3. **工具社区** - 图形化工具:[MongoDB Compass](https://www.mongodb.com/products/compass) - 问题解决:[Stack Overflow MongoDB板块](https://stackoverflow.com/questions/tagged/mongodb) --- ### **三、学习建议流程** ```mermaid graph LR A[基础概念] --> B[CRUD操作] B --> C[聚合管道] C --> D[索引优化] D --> E[模式设计] E --> F[复制集/分片] ``` > 提示:先通过官方交互教程实践基础操作,再结合项目需求深入学习特定模块[^2][^3]。 --- ### 常见问题解答 **Q:如何快速迁移SQL数据到MongoDB?** A:使用`mongoimport`工具或[数据迁移指南](https://www.mongodb.com/docs/database-tools/mongoimport/) **Q:事务支持如何?** A:v4.0+支持多文档ACID事务,但需注意性能影响(参考[事务文档](https://www.mongodb.com/docs/manual/core/transactions/)) ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值