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

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介: 本节书摘来自华章社区《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
相关文章
|
21天前
|
SQL 数据库 微服务
微服务03,最简单的Demo,我们每个服务不能重复开发相同业务,微服务数据独立,不要访问其他微服务的数据库,微服务的特点之一是提供不能功能的数据库互相分割,微服务需要根据业务模块拆分,做到单一职责,
微服务03,最简单的Demo,我们每个服务不能重复开发相同业务,微服务数据独立,不要访问其他微服务的数据库,微服务的特点之一是提供不能功能的数据库互相分割,微服务需要根据业务模块拆分,做到单一职责,
|
2天前
|
存储 关系型数据库 MySQL
MySQL数据库开发进阶:精通数据库表的创建与管理22
【7月更文挑战第22天】数据库的创建与删除,数据表的创建与管理
11 1
|
10天前
|
DataWorks NoSQL fastjson
DataWorks操作报错合集之DataX进行MongoDB全量迁移的过程中,DataX的MongoDB Reader插件在初始化阶段找不到Fastjson 2.x版本的类库,该怎么办
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
10天前
|
Oracle 关系型数据库 Java
实时计算 Flink版操作报错合集之cdc postgres数据库,当表行记录修改后报错,该如何修改
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
10天前
|
SQL 监控 关系型数据库
实时计算 Flink版操作报错合集之在设置监控PostgreSQL数据库时,将wal_level设置为logical,出现一些表更新和删除操作报错,怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
10天前
|
关系型数据库 Java 数据库
实时计算 Flink版操作报错合集之flinksql采PG数据库时报错,该如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
10天前
|
关系型数据库 MySQL 数据库
实时计算 Flink版操作报错合集之在处理PostgreSQL数据库遇到报错。该如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
10天前
|
消息中间件 关系型数据库 数据库
实时计算 Flink版操作报错合集之在使用RDS数据库作为源端,遇到只能同步21个任务,是什么导致的
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
18天前
|
SQL 数据库 Python
Django框架数据库ORM查询操作(6)
【7月更文挑战第6天】```markdown Django ORM常用数据库操作:1) 查询所有数据2) 根据ID查询 3) 精确查询 4) 分页排序
25 1
|
22天前
|
SQL 自然语言处理 网络协议
【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)
TCP(Transmission Control Protocol)连接是互联网上最常用的一种面向连接、可靠的、基于字节流的传输层通信协议。建立TCP连接需要经过著名的“三次握手”过程: 1. SYN(同步序列编号):客户端发送一个SYN包给服务器,并进入SYN_SEND状态,等待服务器确认。 2. SYN-ACK:服务器收到SYN包后,回应一个SYN-ACK(SYN+ACKnowledgment)包,告诉客户端其接收到了请求,并同意建立连接,此时服务器进入SYN_RECV状态。 3. ACK(确认字符):客户端收到服务器的SYN-ACK包后,发送一个ACK包给服务器,确认收到了服务器的确
147 1