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

    xiaoxiao2021-07-15  151

    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 keyMongoDB支持的数据类型中,_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() 来查询,返回一个任意结构的集合,而如何实现特定的查询稍后讲解。

    普通查询一般的查询可以通过 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(), 可能得不到游标里的数据,所以要明确地锁定要查询的游标。

    条件查询下面的示例说明了如何执行一个类似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”的值并没有返回。

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

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

    > printjson(db.things.findOne({name:"mongo"})); { "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" } 通过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 }

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


    最新回复(0)