《MongoDB管理与开发精要》——2.5节操作数据库

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 本节书摘来自华章社区《MongoDB管理与开发精要》一书中的第2章,第2.5节操作数据库,作者:红 丸,更多章节内容可以访问云栖社区“华章社区”公众号查看

2.5 操作数据库
现在就可以使用自带的MongoDB Shell工具来操作数据库了。 当然,也可以使用各种编程语言的驱动来使用MongoDB, 但自带的MongoDB Shell工具可以方便地管理数据库。
2.5.1 连接数据库
打开一个Session输入“/Apps/mongo/bin/mongo”,如果出现下面的提示,说明已经连接上数据库,可以执行操作了:

[root@localhost ~]# /Apps/mongo/bin/mongo
MongoDB shell version: 1.8.1
connecting to: test
>

默认 Shell 连接的是本机localhost 上的 test库,“connecting to:”会显示正在使用的数据库名称,想换数据库的话可以用“use mydb”来实现。
2.5.2 插入记录
建立一个things的集合并写入一些数据,建立两个对象j和t , 并保存到集合中,如下面的代码所示(其中“>”为 Shell 输入提示符):

> j = { name : "mongo" };
{"name" : "mongo"}
> t = { x : 3 };
{ "x" : 3  }
//插入数据
> db.things.save(j);
> db.things.save(t);
> db.things.find();
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
>

以下几点需要注意:
q 不需要预先创建一个集合,在第一次插入数据时会自动创建。
q 在文档中可以存储任何结构的数据, 但在实际应用中存储的还是相同类型文档的集合。此特性很灵活, 不需要类似alter table 语句来修改数据结构。
q 每次插入数据时,集合中都会有一个ID(_id)。
下面插入一些数据,如下面的代码所示:

//用for循环来插入数据
> for( var i = 1; i < 10; i++ ) db.things.save( { x:4, j:i } ); 
> db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
请注意, 这里循环次数是10, 但是只显示到第8条, 还有2条数据没有显示。如果想继续查询下面的数据只需要使用“it”命令,如下面的代码所示:
{ "_id" : ObjectId("4c220a42f3924d31102bd866"), "x" : 4, "j" : 17 }
{ "_id" : ObjectId("4c220a42f3924d31102bd867"), "x" : 4, "j" : 18 }
has more
//此处用it执行继续查询
> it
{ "_id" : ObjectId("4c220a42f3924d31102bd868"), "x" : 4, "j" : 19 }
{ "_id" : ObjectId("4c220a42f3924d31102bd869"), "x" : 4, "j" : 20 }

从技术上讲, find() 返回一个游标对象,但在上面的例子里, 并没有拿到一个游标的变量,所以 Shell 自动遍历游标,返回一个初始化的set,并允许继续用 it 迭代输出。当然,也可以直接用游标来输出,不过这是“游标”部分的内容了。
扩展阅读 什么是_id key
MongoDB支持的数据类型中,_id是其自有产物。存储在MongoDB集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,它可以是MongoDB支持的任何数据类型,默认是ObjectId。在关系数据库Schema设计中,主键大多数是数值型的,比如常用的int和long,并且通常主键的取值由数据库自增获得,这种主键数值的有序性有时也表明了某种逻辑。而MongoDB在设计之初就定位于分布式存储系统,所以它不支持自增主键。
当向一个集合中写入一条文档时,系统会自动生成一个名为_id 的key,如下面的代码所示:

> db.c1.find()
{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }
{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }

这里多出一个类型为ObjectId的key值,在插入时并没有指定,类似Oracle的rowid信息,这属于自动生成的。
在MongoDB中,每一个集合都必须有一个_id字段,字段类型默认是ObjectId。注意,可以不是ObjectId,例如下面的_id=3的条目:

> db.c1.find()
{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }
{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }
{ "_id" : 3, "name" : "Bill", "age" : 55 }

虽然_id的类型可以自由指定,但是在同一个集合中必须唯一,如果插入重复的值,系统将会抛出异常,如下面的代码所示:

> db.c1.insert({_id:3, name:"Bill_new", age:55})
E11000 duplicate key error index: test.c1.$_id_  dup key: { : 3.0 }
>

因为前面已经插入了一条_id=3的记录,所以再插入相同的文档就不允许了。
2.5.3 查询记录
在没有深入讲解查询之前, 先看看怎样从查询中返回一个游标对象。可以简单地通过 find() 来查询,返回一个任意结构的集合,而如何实现特定的查询稍后讲解。

  1. 普通查询
    一般的查询可以通过 while 循环输出,如下面的代码所示:
> var cursor = db.things.find();
> while (cursor.hasNext()) printjson(cursor.next());
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }

上面的例子显示了游标风格的迭代输出,hasNext() 函数用于判断是否还有数据,如果有则调用 next() 函数将数据取出来。
当使用的是 JavaScript Shell时,可以用JavaScript的forEach特性,这样就可以输出游标了。下面的例子就是使用 forEach() 循环输出数据,但forEach() 必须定义一个函数供每个游标元素调用,如下面的代码所示:

> db.things.find().forEach(printjson);
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }
在 MongoDB Shell 里,也可以把游标当成数组来用,如下面的代码所示:
> var cursor = db.things.find();
> printjson(cursor[4]);
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }

使用游标时请注意占用内存的问题,特别是很大的游标对象,有可能会内存溢出,所以应该用迭代的方式来输出。下面的示例是把游标转换成真实的数组类型:

> var arr = db.things.find().toArray();
> arr[5];
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }

注意 这些特性只是在MongoDB Shell里使用,不是所有的其他应用程序驱动都支持。如果有其他用户在集合里第一次或者最后一次调用next(), 可能得不到游标里的数据,所以要明确地锁定要查询的游标。

  1. 条件查询
    下面的示例说明了如何执行一个类似SQL的查询,并演示了如何在 MongoDB 里实现,这是在MongoDB Shell里查询,当然,也可以用其他的应用程序驱动或者语言来实现。

查询条件是 { a:A, b:B, ... } ,类似“where a==A and b==B and ...”。例如,SQL查询:

SELECT * FROM things WHERE x=4
用MongoDB 实现:
> db.things.find({x:4}).forEach(printjson);
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }
上面显示的是所有元素,也可以返回特定的元素,类似于返回表里某字段的值,只需要在“find({x:4})”里指定元素的名称,如下所示:
> db.things.find({x:4}, {j:true}).forEach(printjson);
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "j" : 5 }

上面查询命令中的“{j:true}”显式地指明结果集只返回列“j”的值,而其他列如“x”的值并没有返回。

  1. findOne()语法
    MongoDB Shell为了避免游标可能带来的开销,提供了一个findOne() 函数。这个函数和 find() 函数一样,不过它返回的是游标里的第一条数据,或者返回null,即空数据。

例如name=“mongo” 可以用很多方法来实现,如用 next() 来循环游标或者当成数组返回第一个元素,而用 findOne() 方法更简单和高效,如下面的代码所示:

> printjson(db.things.findOne({name:"mongo"}));
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
  1. 通过limit限制结果集数量
    如果需要限制结果集的长度,可以调用limit方法,如下面的代码所示:
> db.things.find().limit(3);
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }

强烈推荐这种解决性能问题的方法,通过限制条数来减少网络传输,同时,limit方法也广泛应用于分页技术中。

2.5.4 修改记录
如果需要修改表中的记录,可以调用update方法。
例如,将列name的值从“mongo”修改为“mongo_new”,如下面的代码所示:

db.things.update({name:"mongo"},{$set:{name:"mongo_new"}});

接下来通过执行“db.things.find()”命令查询列name的值是否改过来了,如下面的代码所示:

> db.things.find(); 
{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }
{ "_id" : ObjectId("4faa9e7bedd27e6d86d86370"), "name" : "mongo_new" }

经验证,name的值已经改为“mongo_new”了。
2.5.5 删除记录
如果要删除表中的记录,可以调用remove方法。
例如,将用户name是“mongo_new”的记录从集合things中删除,如下面的代码所示:

> db.things.remove({name:"mongo_new"});
> db.things.find();                    
{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }

经验证,该记录确实被删除了。

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
3天前
|
NoSQL MongoDB Redis
Python与NoSQL数据库(MongoDB、Redis等)面试问答
【4月更文挑战第16天】本文探讨了Python与NoSQL数据库(如MongoDB、Redis)在面试中的常见问题,包括连接与操作数据库、错误处理、高级特性和缓存策略。重点介绍了使用`pymongo`和`redis`库进行CRUD操作、异常捕获以及数据一致性管理。通过理解这些问题、易错点及避免策略,并结合代码示例,开发者能在面试中展现其技术实力和实践经验。
23 8
Python与NoSQL数据库(MongoDB、Redis等)面试问答
|
JavaScript 关系型数据库 MySQL
❤Nodejs 第六章(操作本地数据库前置知识优化)
【4月更文挑战第6天】本文介绍了Node.js操作本地数据库的前置配置和优化,包括处理接口跨域的CORS中间件,以及解析请求数据的body-parser、cookie-parser和multer。还讲解了与MySQL数据库交互的两种方式:`createPool`(适用于高并发,通过连接池管理连接)和`createConnection`(适用于低负载)。
10 0
|
5天前
|
SQL JavaScript API
❤Nodejs 第四章(操作本地数据库实现删除-源码地址已开放)
【4月更文挑战第4天】❤Nodejs 第四章(操作本地数据库实现删除-源码地址已开放)在Node.js中实现删除本地数据库记录的操作。首先尝试通过SQL删除ID为8的用户,然后编写`app.delete`路由处理程序,从请求体获取ID并执行删除。。最终成功删除用户并展示了数据库的更新结果。下一节将优化增删改查功能。
18 1
|
13天前
|
存储 关系型数据库 MySQL
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
|
20天前
|
缓存 NoSQL 关系型数据库
【MongoDB】MongoDB更新操作时是否立刻fsync到磁盘?
【4月更文挑战第2天】【MongoDB】MongoDB更新操作时是否立刻fsync到磁盘?
|
21天前
|
消息中间件 NoSQL Kafka
云原生最佳实践系列 5:基于函数计算 FC 实现阿里云 Kafka 消息内容控制 MongoDB DML 操作
该方案描述了一个大数据ETL流程,其中阿里云Kafka消息根据内容触发函数计算(FC)函数,执行针对MongoDB的增、删、改操作。
|
1月前
|
SQL 数据库连接 数据库
你不知道ADo.Net中操作数据库的步骤【超详细整理】
你不知道ADo.Net中操作数据库的步骤【超详细整理】
16 0
|
1月前
|
缓存 NoSQL 数据库
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
[Redis]——数据一致性,先操作数据库,还是先更新缓存?
|
1月前
|
SQL 存储 关系型数据库
【mysql】—— 数据库的操作
【mysql】—— 数据库的操作
【mysql】—— 数据库的操作
|
1月前
|
NoSQL 网络协议 MongoDB
Windows公网远程连接MongoDB数据库【无公网IP】
Windows公网远程连接MongoDB数据库【无公网IP】