现在的位置: 首页 > 综合 > 正文

学习MongoDB–(7-2):进阶指南(GridFS & 服务器端脚本 & 数据库引用)

2013年03月05日 ⁄ 综合 ⁄ 共 5155字 ⁄ 字号 评论关闭

【GridFS】

GridFS是一个建立在普通MongoDB基础上的轻量级文件存储系统。GridFS的一个基本思想就是将大的文件分成很多块,每块作为一个单独的文档进行存储(MongoDB本身支持在文档中存储二进制数据)。除了这些分拆的数据文档外,还有一个单独的文档用于记录各个存储块的相关信息和文件的元数据信息。那如何使用GridFS呢?

使用GridFS的方法最简单的就是利用mongofiles实用程序(就是一个GridFS的客户端)。mongofiles内置在MongoDB的发布版中,可以通过它在GridFS中上传、下载、列示、查找、删除文件。其对应的操作命令是:put,get、list、search、delete,我们分别演示一下:

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles put E:\mongodb\mongofiles\file1.txt
connected to: 127.0.0.1
added file: { _id: ObjectId('503829eefb3029a75e1ec5ac'), filename: "E:\mongodb\mongofiles\file1.txt", chunkSize: 262144, uploadDate: new Date(13458580
31228), md5: "3eb9198f3d7013e253faf411be510cf6", length: 169 }
done!

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles list
connected to: 127.0.0.1
E:\mongodb\mongofiles\file1.txt 169

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles get E:\mongodb\mongofiles\file1.txt
connected to: 127.0.0.1
done write to: E:\mongodb\mongofiles\file1.txt

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles search file1
connected to: 127.0.0.1
E:\mongodb\mongofiles\file1.txt 169

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles delete E:\mongodb\mongofiles\file1.txt
connected to: 127.0.0.1
done!

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles list
connected to: 127.0.0.1

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>

 上面演示的就是使用mongofiles命令,但我们看到put命令将文件存入到数据库中,默认使用当前的文件名作为文件在GridFS中的文件名,get操作默认将文件写入到文件名对应的本地磁盘系统中(如果原始文件还存在,则覆盖)。我们可以通过选项--local(或简写-l)来改变这种行为:(可以通过mongofiles --help查看更多选项)

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles --local  E:\mongodb\mongofiles\file1.txt put file1.txt
connected to: 127.0.0.1
added file: { _id: ObjectId('50382cdff741bb4fae624d60'), filename: "file1.txt", chunkSize: 262144, uploadDate: new Date(1345858783427), md5: "3eb9198f
3d7013e253faf411be510cf6", length: 169 }
done!

E:\mongodb\mongodb-win32-x86_64-2.0.6\bin>mongofiles --local D:\test\file2.txt get file1.txt
connected to: 127.0.0.1
done write to: D:\test\file2.txt

 

上面,我们将本地E盘的一个文件file1.txt存入到GridFS中,重新命名为file1.txt,然后又将这个文件下载保存为本地D盘的file2.txt文件。这里需要注意,通过mongofiles下载文件到特定目录中,这个目录必须存在,否则下载失败!

上面提到了GridFS存储大文件是分块进行的,GridFS默认是将这些存储块信息的文档放置在fs.chunks集合中,将文件的元数据文档放置在fs.files集合中。这两个集合都在test数据库中,我们可以看一下:

 

> use test
switched to db test
> db.fs.files.find();
{ "_id" : ObjectId("50382cdff741bb4fae624d60"), "filename" : "file1.txt", "chunkSize" : 262144, "uploadDate" : ISODate("2012-08-25T01:39:43.427Z"), "m
d5" : "3eb9198f3d7013e253faf411be510cf6", "length" : 169 }
> db.fs.chunks.find();
{ "_id" : ObjectId("50382cdf79f437ddd142a048"), "files_id" : ObjectId("50382cdff741bb4fae624d60"), "n" : 0, "data" : BinData(0,"sLK+srXYt73I+MCtyPi12b
eiy821vcj2tam3ycj3tcS/p7fIyPe1xMj2tdi3vcj4t9LI9rXYt73I9rWpt8nI97fJtbnI+LfJZA0KsKLLubbZt6LLzbfJyPdzYWRmYXNkZg0KDQphc2RmYXNkZg0KDQrI9rXYt73I9rWpt6LLzbW9
ICANCg0KDQrI9rWpt6jI/bj20NDStc7byL66zbj2yMu5/szYDQoNCg==") }
>

 

知道了这两个集合,其实我们在shell中就可以查询目前GridFS中都存储了哪些文件!上述两个集合,有些键需要注意,在集合fs.chunks中,有一个键"files_id"其值就是其对应的文件元数据文档在集合fs.files中的“_id”的值;键“n”就是这个块在大文件分成的所有块中排第几个;键“data”就是这个块的数据了。集合fs.files中有一个键"md5",表示整个文件对应的md5的值,用户可以通过这个值来校验文件数据的完整性!

GridFS底层利用的就是MongoDB来存储大文件,会直接使用业已建立好的复制和分片机制来保证文件的可用性。

【服务器端脚本】

在MongoDB的服务器端可以通过db.eval函数来执行javascript脚本,如我们可以定义一个javascript函数,然后通过db.eval在服务器端来运行!我们前面其实也接触过在服务器段运行一个预定义的javascript脚本的情况,如在$where查询,执行mapreduce任务等。我们先看看db.eval是如何使用的:

> db.eval("return 1");
1
> db.eval("function(){return 9 + 8}");
17
> db.eval("function(x,y,z,k){return x*y + z*k}", 2,5, 8, 10);
90
>

对于db.eval,如果接受的javascript串,不需要什么参数,就可以直接书写!需要传递参数,就可以定义成函数,然后在调用时,eval函数从第二个参数起就分别代表需要传递给脚本的参数!

每个MongoDB数据库都会有一个特殊的结合system.js,用来存在javascript变量和脚本,这些变量和脚本可以在db.eval调用、$where子句、mapreduce任务中直接使用:

> use mylearndb;
switched to db mylearndb
> db.system.js.insert({"_id":"x", "value":11});
> db.system.js.insert({"_id":"y", "value":10});
> db.system.js.insert({"_id":"z", "value":9});
> db.system.js.insert({"_id":"k", "value":7});
> db.eval("return x*y + z*k");
173
>

上例中,我们往system.js中保存了几个变量,然后可以在db.eval中直接使用!我们同样可以将函数保存在system.js中,对于system.js中的文档,"_id"表示的是变量名或函数名,“value”表示的是变量值或函数定义!对于一些常用的函数,我们可以将其存储在system.js集合中,这样可以进行将某些业务逻辑进行封装统一管理,也减少了网络传输时间(感觉这个非常像关系型数据库的存储过程!)。

使用服务器端脚本,如果需要客户端传递参数,需要严格处理这些参数,增强安全性!防止类似于关系型数据库中的脚本注入式攻击的出现!

【数据库引用】

这是MongoDB中鲜为人知的一个功能,也叫做DBRef,其就像关系型数据库中的外键的概念,让一个文档引用另外一个或多个其他文档。

DBRef的使用形式是一个内嵌文档,和其他内嵌文档的结构也一致。但这个内嵌文档有一些必选键,"$ref":指示一个集合,"$id":具体指示一条文档。如果该文档引用的文档在其他数据库中,这个内嵌文档也支持一个可选键:"$db":指示一个数据库!注意这个内嵌文档中键的顺序不能改变:第一个必须是"$ref",紧接着是"$id",然后是可选的"$db"。我们看一个例子,有两个集合,users和notes,用户(user)可以创建笔记(note),在notes集合中,每条笔记会引用一个用户:

> db.users.find();
{ "_id" : "01", "name" : "jimmy", "email" : "jimmy@csdn.net", "age" : 23 }
{ "_id" : "02", "name" : "tim", "email" : "tim@csdn.net", "age" : 33 }
> db.notes.find();
{ "_id" : "001", "content" : "Mongo is fun", "references" : { "$ref" : "users", "$id" : "01" } }
{ "_id" : "002", "content" : "Mongo is too hard to learn", "references" : { "$ref" : "users", "$id" : "02" } }
> var note = db.notes.findOne({"_id":"001"});
> printjson(db[note.references.$ref].findOne({"_id":note.references.$id}));
{ "_id" : "01", "name" : "jimmy", "email" : "jimmy@csdn.net", "age" : 23 }
>

 

通过DBRef的使用,我们可以在应用层将一个文档引用的其他文档得到!但如果都是在应用层如上处理,我们何必要使用DBRef呢?我们完全可以自定义一种更轻量级的引用方式,何必要写成内嵌文档的格式,而且还有必选键?

选择使用DBRef的一个原因是,各类数据库驱动对DBRef的一些内置的支持,部分数据库驱动甚至将DBRef作为一种特殊的类型来使用!还有,如果我们要引用的文档来自另一个数据库中,DBRef这种方式也算一种比较紧凑的方式了,可以考虑直接使用。

我们上面提到的GridFS中,就有文档引用这种情况,集合fs.chunks中的文档会引用fs.files中的文档,其没有采用DBRef这种方式,而是直接使用了键“files_id”来进行最直接的引用。采用这种方式的原因是,被引用的文档的集合在同一个数据库中并且固定,这种情况下,我们没必要使用DBRef这种稍显复杂的方式。

抱歉!评论已关闭.