MongoDB学习笔记

MongoDB学习笔记

转自: http://www.wdcode.org/archives/458.html


这是MongoDB的系列学习笔记的第一篇,主要介绍什么是非关系型数据库MongoDB,如何下载,去哪儿下载,又该怎么正确的安装等一系列问题。

一、前言

最近开始学习非关系型数据库MongoDB,却在博客园上找不到比较系统的教程,很多资料都要去查阅英文网站,效率比较低下。本人不才,借着自学的机会把心得体会都记录下来,方便感兴趣的童鞋分享讨论。部分资源出自其他博客,旨将零散知识点集中到一起,如果有侵犯您的权利,请联系li-pan2@163.com。大部分内容均系原创,欢迎大家转载分享,但转载的同时别忘了注明作者和原文链接哦。

二、MongoDB简介

MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式。Mongo使用C++开发。Mongo的官方网站地址是:http://www.mongodb.org/,读者可以在此获得更详细的信息。

小插曲:什么是NoSql?

NoSql,全称是 Not Only Sql,指的是非关系型的数据库。下一代数据库主要解决几个要点:非关系型的、分布式的、开源的、水平可扩展的。原始的目的是为了大规模web应用,这场运动开始于2009年初,通常特性应用如:模式自由、支持简易复制、简单的API、最终的一致性(非ACID)、大容量数据等。NoSQL被我们用得最多的当数key-value存储,当然还有其他的文档型的、列存储、图型数据库、xml数据库等。

特点:

  •  

  • 高性能、易部署、易使用,存储数据非常方便。主要功能特性有:
  • 面向集合存储,易存储对象类型的数据。
  • 模式自由。
  • 支持动态查询。
  • 支持完全索引,包含内部对象。
  • 支持查询。
  • 支持复制和故障恢复。
  • 使用高效的二进制数据存储,包括大型对象(如视频等)。
  • 自动处理碎片,以支持云计算层次的扩展性
  • 支持Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++语言的驱动程序,社区中也提供了对Erlang及.NET等平台的驱动程序。
  • 文件存储格式为BSON(一种JSON的扩展)。
  • 可通过网络访问。

     

功能:

  •  

  • 面向集合的存储:适合存储对象及JSON形式的数据。
  • 动态查询:Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
  • 完整的索引支持:包括文档内嵌对象及数组。Mongo的查询优化器会分析查询表达式,并生成一个高效的查询计划。
  • 查询监视:Mongo包含一个监视工具用于分析数据库操作的性能。
  • 复制及自动故障转移:Mongo数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制。复制的主要目标是提供冗余及自动故障转移。
  • 高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)
  • 自动分片以支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器。

     

适用场合:

  •  

  • 网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
  • 缓存:由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源 过载。
  • 大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储。
  • 高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。
  • 用于对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储及查询。

     

 

三、下载安装和配置

安装Mongo数据库:

在发布本文的时间官方提供的最新版本是:1.6.5 ,如果不做特殊声明,本教程所用的版本将会是这个版本。

  1. 第一步:下载安装包:如果是win系统,注意是64位还是32位版本的,请选择正确的版本。
  2. 第二步:新建目录“D:\MongoDB”,解压下载到的安装包,找到bin目录下面全部.exe文件,拷贝到刚创建的目录下。
  3. 第三步:在“D:\MongoDB”目录下新建“data”文件夹,它将会作为数据存放的根文件夹。

注:官方下载地址:http://www.mongodb.org/downloads

配置Mongo服务端:

打开CMD窗口,按照如下方式输入命令:
> d:
> cd D:\MongoDB
> mongod –dbpath D:\MongoDB\data

配置成功后会看到如下画面:


在浏览器输入:http://localhost:27017/,可以看到如下提示:
You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number

如此,MongoDB数据库服务已经成功启动了。

传统的关系数据库一般由数据库(database)、表(table)、记录(record)三个层次概念组成,MongoDB是由(database)、集合(collection)、文档对象(document)三个层次组成。MongoDB对于关系型数据库里的表,但是集合中没有列、行和关系概念,这体现了模式自由的特点。

一、关于MongoDB的驱动

MongoDB支持多种语言的驱动,在此我们只介绍C#的驱动。仅C#驱动都有很多种,每种驱动的形式大致相同,但是细节各有千秋,因此代码不能通用。比较常用的是官方驱动和samus驱动。samus驱动除了支持一般形式的操作之外,还支持linq方式操纵数据。各人比较喜欢这种方式。

官方驱动下载地址:https://github.com/mongodb/mongo-csharp-driver/downloads

samus驱动下载地址:https://github.com/samus/mongodb-csharp

本篇将从samus驱动入手讲解数据库访问,国际惯例,存取“Hello World!”。

二、通过samus驱动实现HelloWorld存取

在进行下述操作之前,请先确定MongoDB服务已经开启,不知道怎么开启服务,请看上篇。下载驱动,新建控制台项目,并添加对MongoDB.dll的引用,如果你下载的是驱动源码,编译一遍引用生成的DLL即可。

基本代码如下:

 
 
  1. //链接字符串
  2. string connectionString = "mongodb://localhost";
  3. //数据库名
  4. string databaseName = "myDatabase";
  5. //集合名
  6. string collectionName = "myCollection";
  7. //定义Mongo服务
  8. Mongo mongo = new Mongo(connectionString);
  9. //获取databaseName对应的数据库,不存在则自动创建
  10. MongoDatabase mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;
  11. //获取collectionName对应的集合,不存在则自动创建
  12. MongoCollection<Document> mongoCollection = mongoDatabase.GetCollection<Document>(collectionName) as MongoCollection<Document>;
  13. //链接数据库
  14. mongo.Connect();
  15. try
  16. {
  17.      //定义一个文档对象,存入两个键值对
  18.      Document doc = new Document();
  19.      doc["ID"] = 1;
  20.      doc["Msg"] = "Hello World!";
  21.      //将这个文档对象插入集合
  22.      mongoCollection.Insert(doc);
  23.      //在集合中查找键值对为ID=1的文档对象
  24.      Document docFind = mongoCollection.FindOne(new Document { { "ID", 1 } });
  25.      //输出查找到的文档对象中键“Msg”对应的值,并输出
  26.      Console.WriteLine(Convert.ToString(docFind["Msg"]));
  27. }
  28. finally
  29. {
  30.      //关闭链接
  31.      mongo.Disconnect();
  32. }

运行程序,成功打印helloword。同时,我们打开数据文件夹,发现多了两个文件“myDatabase.ns”和“myDatabase.0”。

看到下图,是通过Jqgrid实现表格数据的基本增删查改的操作。表格数据增删改是一般企业应用系统开发的常见功能,不过不同的是这个表格数据来源是非关系型的数据库MongoDB。nosql虽然概念新颖,但是MongoDB基本应用实现起来还是比较轻松的,甚至代码比基本的ADO.net访问关系数据源还要简洁。由于其本身的“非关系”的数据存储方式,使得对象关系映射这个环节对于MongoDB来讲显得毫无意义,因此我们也不会对MongoDB引入所谓的“ORM”框架。


下面我们将逐步讲解怎么在MVC模式下将MongoDB数据读取,并展示在前台Jqgrid表格上。这个“简易系统”的基本设计思想是这样的:我们在视图层展示表格,Jqgrid相关Js逻辑全部放在一个Js文件中,控制层实现了“增删查改”四个业务,MongoDB的基本数据访问放在了模型层实现。下面我们一步步实现。

一、实现视图层Jqgrid表格逻辑

首先,我们新建一个MVC空白项目,添加好jQuery、jQueryUI、Jqgrid的前端框架代码:

然后在Views的Home文件夹下新建视图“Index.aspx”,在视图的body标签中添加如下HTML代码:

 
 
  1. <div>
  2.     <table id="table1">
  3.     </table>
  4.     <div id="div1">
  5.     </div>
  6. </div>

接着新建Scripts\Home文件夹,在该目录新建“Index.js”文件,并再视图中引用,代码如下:

 
 
  1. jQuery(document).ready(function () {
  2.     //jqGrid初始化
  3.     jQuery("#table1").jqGrid({
  4.         url: '/Home/UserList',
  5.         datatype: 'json',
  6.         mtype: 'POST',
  7.         colNames: ['登录名', '姓名', '年龄', '手机号', '邮箱地址', '操作'],
  8.         colModel: [
  9.              { name: 'UserId', index: 'UserId', width: 180, editable: true },
  10.              { name: 'UserName', index: 'UserName', width: 200, editable: true },
  11.              { name: 'Age', index: 'Age', width: 150, editable: true },
  12.              { name: 'Tel', index: 'Tel', width: 150, editable: true },
  13.              { name: 'Email', index: 'Email', width: 150, editable: true },
  14.              { name: 'Edit', index: 'Edit', width: 150, editable: false, align: 'center' }
  15.              ],
  16.         pager: '#div1',
  17.         postData: {},
  18.         rowNum: 5,
  19.         rowList: [5, 10, 20],
  20.         sortable: true,
  21.         caption: '用户信息管理',
  22.         hidegrid: false,
  23.         rownumbers: true,
  24.         viewrecords: true
  25.     }).navGrid('#div1', { edit: false, add: false, del: false })
  26.             .navButtonAdd('#div1', {
  27.                 caption: "编辑",
  28.                 buttonicon: "ui-icon-add",
  29.                 onClickButton: function () {
  30.                     var id = $("#table1").getGridParam("selrow");
  31.                     if (id == null) {
  32.                         alert("请选择行!");
  33.                         return;
  34.                     }
  35.                     if (id == "newId") return;
  36.                     $("#table1").editRow(id);
  37.                     $("#table1").find("#" + id + "_UserId").attr("readonly","readOnly");
  38.                     $("#table1").setCell(id, "Edit", "<input id='Button1' type='button' value='提交' οnclick='Update(\"" + id + "\")' /><input id='Button2' type='button' value='取消' οnclick='Cancel(\"" + id + "\")' />");
  39.                 }
  40.             }).navButtonAdd('#div1', {
  41.                 caption: "删除",
  42.                 buttonicon: "ui-icon-del",
  43.                 onClickButton: function () {
  44.                     var id = $("#table1").getGridParam("selrow");
  45.                     if (id == null) {
  46.                         alert("请选择行!");
  47.                         return;
  48.                     }
  49.                     Delete(id);
  50.                 }
  51.             }).navButtonAdd('#div1', {
  52.                 caption: "新增",
  53.                 buttonicon: "ui-icon-add",
  54.                 onClickButton: function () {
  55.                     $("#table1").addRowData("newId", -1);
  56.                     $("#table1").editRow("newId");
  57.                     $("#table1").setCell("newId", "Edit", "<input id='Button1' type='button' value='提交' οnclick='Add()' /><input id='Button2' type='button' value='取消' οnclick='Cancel(\"newId\")' />");
  58.                 }
  59.             });
  60. });
  61. //取消编辑状态
  62. function Cancel(id) {
  63.     if (id == "newId") $("#table1").delRowData("newId");
  64.     else $("#table1").restoreRow(id);
  65. }
  66. //向后台ajax请求新增数据
  67. function Add() {
  68.     var UserId = $("#table1").find("#newId" + "_UserId").val();
  69.     var UserName = $("#table1").find("#newId" + "_UserName").val();
  70.     var Age = $("#table1").find("#newId" + "_Age").val();
  71.     var Tel = $("#table1").find("#newId" + "_Tel").val();
  72.     var Email = $("#table1").find("#newId" + "_Email").val();
  73.     $.ajax({
  74.         type: "POST",
  75.         url: "/Home/Add",
  76.         data: "UserId=" + UserId + "&UserName=" + UserName + "&Age=" + Age + "&Tel=" + Tel + "&Email=" + Email,
  77.         success: function (msg) {
  78.             alert("新增数据: " + msg);
  79.             $("#table1").trigger("reloadGrid");
  80.         }
  81.     });
  82. }
  83. //向后台ajax请求更新数据
  84. function Update(id) {
  85.     var UserId = $("#table1").find("#" + id + "_UserId").val();
  86.     var UserName = $("#table1").find("#" + id + "_UserName").val();
  87.     var Age = $("#table1").find("#" + id + "_Age").val();
  88.     var Tel = $("#table1").find("#" + id + "_Tel").val();
  89.     var Email = $("#table1").find("#" + id + "_Email").val();
  90.     $.ajax({
  91.         type: "POST",
  92.         url: "/Home/Update",
  93.         data: "UserId=" + UserId + "&UserName=" + UserName + "&Age=" + Age + "&Tel=" + Tel + "&Email=" + Email,
  94.         success: function (msg) {
  95.             alert("修改数据: " + msg);
  96.             $("#table1").trigger("reloadGrid");
  97.         }
  98.     });
  99. }
  100. //向后台ajax请求删除数据
  101. function Delete(id) {
  102.     var UserId = $("#table1").getCell(id, "UserId");
  103.     $.ajax({
  104.         type: "POST",
  105.         url: "/Home/Delete",
  106.         data: "UserId=" + UserId,
  107.         success: function (msg) {
  108.             alert("删除数据: " + msg);
  109.             $("#table1").trigger("reloadGrid");
  110.         }
  111.     });
  112. }

二、实现控制层业务

在Controllers目录下新建控制器“HomeController.cs”,Index.js中产生了四个ajax请求,对应控制层也有四个业务方法。HomeController代码如下:

 
 
  1. public class HomeController : Controller
  2. {
  3.     UserModel userModel = new UserModel();
  4.     public ActionResult Index()
  5.     {
  6.         return View();
  7.     }
  8.     /// <summary>
  9.     /// 获取全部用户列表,通过json将数据提供给jqGrid
  10.     /// </summary>
  11.     public JsonResult UserList(string sord, string sidx, string rows, string page)
  12.     {
  13.         var list = userModel.FindAll();
  14.         int i = 0;
  15.         var query = from u in list
  16.                     select new
  17.                     {
  18.                         id = i++,
  19.                         cell = new string[]{
  20.                             u["UserId"].ToString(),
  21.                             u["UserName"].ToString(),
  22.                             u["Age"].ToString(),
  23.                             u["Tel"].ToString(),
  24.                             u["Email"].ToString(),
  25.                             "-"
  26.                         }
  27.                     };
  28.         var data = new
  29.         {
  30.             total = query.Count() / Convert.ToInt32(rows) + 1,
  31.             page = Convert.ToInt32(page),
  32.             records = query.Count(),
  33.             rows = query.Skip(Convert.ToInt32(rows) * (Convert.ToInt32(page) - 1)).Take(Convert.ToInt32(rows))
  34.         };
  35.         return Json(data, JsonRequestBehavior.AllowGet);
  36.     }
  37.     /// <summary>
  38.     /// 响应Js的“Add”ajax请求,执行添加用户操作
  39.     /// </summary>
  40.     public ContentResult Add(string UserId, string UserName, int Age, string Tel, string Email)
  41.     {
  42.         Document doc = new Document();
  43.         doc["UserId"] = UserId;
  44.         doc["UserName"] = UserName;
  45.         doc["Age"] = Age;
  46.         doc["Tel"] = Tel;
  47.         doc["Email"] = Email;
  48.         try
  49.         {
  50.             userModel.Add(doc);
  51.             return Content("添加成功");
  52.         }
  53.         catch
  54.         {
  55.             return Content("添加失败");
  56.         }
  57.     }
  58.     /// <summary>
  59.     /// 响应Js的“Delete”ajax请求,执行删除用户操作
  60.     /// </summary>
  61.     public ContentResult Delete(string UserId)
  62.     {
  63.         try
  64.         {
  65.             userModel.Delete(UserId);
  66.             return Content("删除成功");
  67.         }
  68.         catch
  69.         {
  70.             return Content("删除失败");
  71.         }
  72.     }
  73.     /// <summary>
  74.     /// 响应Js的“Update”ajax请求,执行更新用户操作
  75.     /// </summary>
  76.     public ContentResult Update(string UserId, string UserName, int Age, string Tel, string Email)
  77.     {
  78.         Document doc = new Document();
  79.         doc["UserId"] = UserId;
  80.         doc["UserName"] = UserName;
  81.         doc["Age"] = Age;
  82.         doc["Tel"] = Tel;
  83.         doc["Email"] = Email;
  84.         try
  85.         {
  86.             userModel.Update(doc);
  87.             return Content("修改成功");
  88.         }
  89.         catch
  90.         {
  91.             return Content("修改失败");
  92.         }
  93.     }
  94. }

三、实现模型层数据访问

最后,我们在Models新建一个Home文件夹,添加模型“UserModel.cs”,实现MongoDB数据库访问代码如下:

 
 
  1. public class UserModel
  2.  {
  3.      //链接字符串(此处三个字段值根据需要可为读配置文件)
  4.      public string connectionString = "mongodb://localhost";
  5.      //数据库名
  6.      public string databaseName = "myDatabase";
  7.      //集合名
  8.      public string collectionName = "userCollection";
  9.      private Mongo mongo;
  10.      private MongoDatabase mongoDatabase;
  11.      private MongoCollection<Document> mongoCollection;
  12.      public UserModel()
  13.      {
  14.          mongo = new Mongo(connectionString);
  15.          mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;
  16.          mongoCollection = mongoDatabase.GetCollection<Document>(collectionName) as MongoCollection<Document>;
  17.          mongo.Connect();
  18.      }
  19.      ~UserModel()
  20.      {
  21.          mongo.Disconnect();
  22.      }
  23.      /// <summary>
  24.      /// 增加一条用户记录
  25.      /// </summary>
  26.      /// <param name="doc"></param>
  27.      public void Add(Document doc)
  28.      {
  29.          mongoCollection.Insert(doc);
  30.      }
  31.      /// <summary>
  32.      /// 删除一条用户记录
  33.      /// </summary>
  34.      public void Delete(string UserId)
  35.      {
  36.          mongoCollection.Remove(new Document { { "UserId", UserId } });
  37.      }
  38.      /// <summary>
  39.      /// 更新一条用户记录
  40.      /// </summary>
  41.      /// <param name="doc"></param>
  42.      public void Update(Document doc)
  43.      {
  44.          mongoCollection.FindAndModify(doc, new Document { { "UserId", doc["UserId"].ToString() } });
  45.      }
  46.      /// <summary>
  47.      /// 查找所有用户记录
  48.      /// </summary>
  49.      /// <returns></returns>
  50.      public IEnumerable<Document> FindAll()
  51.      {
  52.          return mongoCollection.FindAll().Documents;
  53.      }
  54.  }
MongoDB的集合(collection)可以看做关系型数据库的表,文档对象(document)可以看做关系型数据库的一条记录。但两者并不完全对等。表的结构是固定的,MongoDB集合并没有这个约束;另外,存入集合的文档对象甚至可以嵌入子文档,或者“子集合”。他们最终都可以用类似于BJSON的格式描述。我们今天就来分析MongoDB这一特性带来的独特数据管理方式。我们还是以samus驱动为例来分析,samus驱动支持两种方式访问数据库,基本方式和linq方式,基本方式在上篇以介绍过,linq方式我不想单独讲解应用实例,这篇我会用两种方式来对比介绍。 一、包含子文档的集合操作 有这么一个应用场景,某网站提供会员登录的功能,用户需要注册账号才能享受会员服务,但是注册者可能会因为用户资料表单输入项过大而放弃填写,因此用户信息分为主要资料和详细资料两项,初次注册只需要填写主要资料就行了。我们打算把详细信息设计为子文档存储。 1) linq方式实现 1. 新建数据描述类,描述用户信息
  
  
  1. /// <summary>
  2. /// 用户主要资料
  3. /// </summary>
  4. public class UserInfo
  5. {
  6.     public string UserId { get; set; }
  7.     public string UserName { get; set; }
  8.     public string PassWord { get; set; }
  9.     public Detail Detail { get; set; }
  10. }
  11. /// <summary>
  12. /// 用户详细资料
  13. /// </summary>
  14. public class Detail
  15. {
  16.     public string Address { get; set; }
  17.     public int Age { get; set; }
  18.     public string Email { get; set; }
  19. }

2. 我们要新建一个用户业务操作类“UserBLL”。这个时候要让驱动知道UserInfo类描述了“用户资料”的字段信息,在GetMongo()方法实现了配置步骤,UserBLL完整代码如下:

  
  
  1. public class UserBLL
  2. {
  3.     public string connectionString = "mongodb://localhost";
  4.     public string databaseName = "myDatabase";
  5.     private Mongo mongo;
  6.     private MongoDatabase mongoDatabase;
  7.     //注意这里泛型类型为“UserInfo”
  8.     private MongoCollection<UserInfo> mongoCollection;
  9.     public UserBLL()
  10.     {
  11.         mongo = GetMongo();
  12.         mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;
  13.         mongoCollection = mongoDatabase.GetCollection<UserInfo>() as MongoCollection<UserInfo>;
  14.         mongo.Connect();
  15.     }
  16.     ~UserBLL()
  17.     {
  18.         mongo.Disconnect();
  19.     }
  20.     /// <summary>
  21.     /// 配置Mongo,将类UserInfo映射到集合
  22.     /// </summary>
  23.     private Mongo GetMongo()
  24.     {
  25.         var config = new MongoConfigurationBuilder();
  26.         config.Mapping(mapping =>
  27.         {
  28.             mapping.DefaultProfile(profile =>
  29.             {
  30.                 profile.SubClassesAre(t => t.IsSubclassOf(typeof(UserInfo)));
  31.             });
  32.             mapping.Map<UserInfo>();
  33.         });
  34.         config.ConnectionString(connectionString);
  35.         return new Mongo(config.BuildConfiguration());
  36.     }
  37. }

3. 接着,在“UserBLL”类中定义一个方法“InsertSomeData()”来插入一些数据:

  
  
  1. /// <summary>
  2. /// 插入一些数据
  3. /// </summary>
  4. public void InsertSomeData()
  5. {
  6.    UserInfo userInfo1 = new UserInfo()
  7.    {
  8.        UserId = "1001",
  9.        UserName = "张三",
  10.        PassWord = "123456"
  11.    };
  12.    mongoCollection.Save(userInfo1);
  13.    UserInfo userInfo2 = new UserInfo()
  14.    {
  15.        UserId = "1002",
  16.        UserName = "李四",
  17.        PassWord = "123456",
  18.        Detail = new Detail()
  19.        {
  20.            Address = "湖北",
  21.            Age = 20,
  22.            Email = "lisi@163.com"
  23.        }
  24.    };
  25.    mongoCollection.Save(userInfo2);
  26.    UserInfo userInfo3 = new UserInfo()
  27.    {
  28.        UserId = "1003",
  29.        UserName = "王五",
  30.        PassWord = "123456",
  31.        Detail = new Detail()
  32.        {
  33.            Address = "广东",
  34.            Age = 20,
  35.            Email = "wangwu@163.com"
  36.        }
  37.    };
  38.    mongoCollection.Save(userInfo3);
  39.    UserInfo userInfo4 = new UserInfo()
  40.    {
  41.        UserId = "1004",
  42.        UserName = "赵六",
  43.        PassWord = "123456",
  44.        Detail = new Detail()
  45.        {
  46.            Address = "湖北"
  47.         }
  48.    };
  49.    mongoCollection.Save(userInfo4);
  50. }

4. 定义一个查找数据的方法“Select”,它将查找用户详细信息中,地址在湖北的全部用户:

  
  
  1. <summary>
  2. /// 查询详细资料地址为湖北的用户信息
  3. /// </summary>
  4. public List<UserInfo> Select()
  5. {
  6.     return mongoCollection.Linq().Where(x => x.Detail.Address == "湖北").ToList();
  7. }

5. 还定义一个删除数据的方法,将删除集合全部数据:

  
  
  1. /// <summary>
  2. /// 删除全部用户信息
  3. /// </summary>
  4. public void DeleteAll()
  5. {
  6.     mongoCollection.Remove(x => true);
  7. }

6. 在Main方法中添加如下代码:

  
  
  1. static void Main(string[] args)
  2. {
  3.     UserBLL userBll = new UserBLL();
  4.     userBll.InsertSomeData();
  5.     var users = userBll.Select();
  6.     foreach (var user in users)
  7.     {
  8.         Console.WriteLine(user.UserName + "是湖北人");
  9.     };
  10.     userBll.DeleteAll();
  11. }

7. 最后执行程序,打印如下信息:
李四是湖北人
赵六是湖北人
1) 普通实现
普通方式实现不想多讲,直接贴代码,看看与linq方式有什么区别:

  
  
  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         UserBLL userBll = new UserBLL();
  6.         userBll.InsertSomeData();
  7.         var users = userBll.Select();
  8.         foreach (var user in users)
  9.         {
  10.             Console.WriteLine(user["UserName"].ToString() + "是湖北人");
  11.         };
  12.         userBll.DeleteAll();
  13.         Console.ReadLine();
  14.     }
  15. }
  16. public class UserBLL
  17. {
  18.     public string connectionString = "mongodb://localhost";
  19.     public string databaseName = "myDatabase";
  20.     public string collectionName = "UserInfo";
  21.     private Mongo mongo;
  22.     private MongoDatabase mongoDatabase;
  23.     private MongoCollection<Document> mongoCollection;
  24.     public UserBLL()
  25.     {
  26.         mongo = new Mongo(connectionString);
  27.         mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;
  28.         mongoCollection = mongoDatabase.GetCollection<Document>(collectionName) as MongoCollection<Document>;
  29.         mongo.Connect();
  30.     }
  31.     ~UserBLL()
  32.     {
  33.         mongo.Disconnect();
  34.     }
  35.     /// <summary>
  36.     /// 插入一些数据
  37.     /// </summary>
  38.     public void InsertSomeData()
  39.     {
  40.         Document userInfo1 = new Document();
  41.         userInfo1["UserId"] = "1001";
  42.         userInfo1["UserName"] = "张三";
  43.         userInfo1["PassWord"] = "123456";
  44.         mongoCollection.Save(userInfo1);
  45.         Document userInfo2 = new Document();
  46.         userInfo2["UserId"] = "1002";
  47.         userInfo2["UserName"] = "李四";
  48.         userInfo2["PassWord"] = "123456";
  49.         //子文档
  50.         var userInfo2Detail = new Document();
  51.         userInfo2Detail["Address"] = "湖北";
  52.         userInfo2Detail["Age"] = 20;
  53.         userInfo2Detail["Email"] = "lisi@163.com";
  54.         userInfo2["Detail"] = userInfo2Detail;
  55.         mongoCollection.Save(userInfo2);
  56.         Document userInfo3 = new Document();
  57.         userInfo3["UserId"] = "1003";
  58.         userInfo3["UserName"] = "王五";
  59.         userInfo3["PassWord"] = "123456";
  60.         var userInfo3Detail = new Document();
  61.         userInfo3Detail["Address"] = "广东";
  62.         userInfo3Detail["Age"] = 20;
  63.         userInfo3Detail["Email"] = "wangwu@163.com";
  64.         userInfo3["Detail"] = userInfo3Detail;
  65.         mongoCollection.Save(userInfo3);
  66.         Document userInfo4 = new Document();
  67.         userInfo4["UserId"] = "1004";
  68.         userInfo4["UserName"] = "赵六";
  69.         userInfo4["PassWord"] = "123456";
  70.         var userInfo4Detail = new Document();
  71.         userInfo4Detail["Address"] = "湖北";
  72.         userInfo4["Detail"] = userInfo4Detail;
  73.         mongoCollection.Save(userInfo4);
  74.     }
  75.     /// <summary>
  76.     /// 查询详细资料地址为湖北的用户信息
  77.     /// </summary>
  78.     public IEnumerable<Document> Select()
  79.     {
  80.         return mongoCollection.Find(new Document { { "Detail.Address", "湖北" } }).Documents;
  81.     }
  82.     /// <summary>
  83.     /// 删除全部用户信息
  84.     /// </summary>
  85.     public void DeleteAll()
  86.     {
  87.         mongoCollection.Remove(new Document { });
  88.     }
  89. }

最后,我们通过这段代码输出全部用户资料信息的BJSON格式:

  
  
  1. /// <summary>
  2. /// 打印数据BJSON
  3. /// </summary>
  4. public void PrintBJSON()
  5. {
  6.     string BJSON = string.Empty;
  7.     foreach (var documet in mongoCollection.FindAll().Documents)
  8.     {
  9.         BJSON += documet.ToString();
  10.     }
  11.     Console.WriteLine(BJSON);
  12. }
  13. 结果如下:
  14. { "UserId": "1001", "UserName": "张三", "PassWord": "123456", "_id": "4d80ec1ab8a4731338000001" }
  15. { "UserId": "1002", "UserName": "李四", "PassWord": "123456", "Detail": { "Address": "湖北", "Age": 20, "Email": "lisi@163.com" }, "_id": "4d80ec1ab8a4731338000002" }
  16. { "UserId": "1003", "UserName": "王五", "PassWord": "123456", "Detail": { "Address": "广东", "Age": 20, "Email": "wangwu@163.com" }, "_id": "4d80ec1ab8a4731338000003" }
  17. { "UserId": "1004", "UserName": "赵六", "PassWord": "123456", "Detail": { "Address": "湖北" }, "_id": "4d80ec1ab8a4731338000004" }
二、包含“子集合”的集合操作 同样举个例子:有一个学校人事管理系统要统计班级和学生的信息,现在定义了一个“班级集合”,这个集合里面的学生字段是一个“学生集合”,包含了本班全部学生。 1) linq方式实现 基础配置我就不多说了,数据类定义如下:
   
   
  1. /// <summary>
  2. /// 班级信息
  3. /// </summary>
  4. public class ClassInfo
  5. {
  6.     public string ClassName { get; set; }
  7.     public List<Student> Students { get; set; }
  8. }
  9. /// <summary>
  10. /// 学生信息
  11. /// </summary>
  12. public class Student
  13. {
  14.     public string Name { get; set; }
  15.     public int Age { get; set; }
  16. }

查询叫“张三”的学生在哪个班级,以及他的详细信息:

(这里其实是ToList后在内存中查的,linq方式直接查询好像驱动不支持。)

   
   
  1. public List<ClassInfo> Select()
  2. {
  3.    return mongoCollection.Linq().ToList().Where(x => x.Students.Exists(s => s.Name == "张三")).ToList();
  4. }

1) 普通实现

查询叫“张三”的学生在哪个班级,以及他的详细信息:

   
   
  1. public List<Document> Select()
  2. {
  3.    var mongocollection = mongoDatabase.GetCollection("ClassInfo");
  4.    return mongocollection.Find(new Document { { "Students.Name", "张三" } }).Documents.ToList();
  5. }

打印数据的BJSON:

   
   
  1. { "_id": "4d814bae5c5f000000005f63", "ClassName": "1001", "Students": [ { "Name": "张三", "Age": 10 }, { "Name": "李四", "Age": 0 } ] }
  2. { "_id": "4d814bae5c5f000000005f64", "ClassName": "1002", "Students": [ ] }
  3. { "_id": "4d814bae5c5f000000005f65", "ClassName": "1003", "Students": [ { "Name": "王五", "Age": 11 }, { "Name": "赵六", "Age": 9 } ] }
 

由于MongoDB的文档结构为BJSON格式(BJSON全称:Binary JSON),而BJSON格式本身就支持保存二进制格式的数据,因此可以把文件的二进制格式的数据直接保存到MongoDB的文档结构中。但是由于一个BJSON的最大长度不能超过4M,所以限制了单个文档中能存入的最大文件不能超过4M。为了提供对大容量文件存取的支持,samus驱动提供了“GridFS”方式来支持,“GridFS”方式文件操作需要引入新的程序集“MongoDB.GridFS.dll”。下面我们分别用两种方式来实现。 一、在文档对象中存取文件 当文件大小较小的时候,直接存入文档对象实现起来更简洁。比如大量图片文件的存取等,一般图片文件都不会超过4M。我们先实现一个上传图片存入数据库,再取出来写回页面的例子: 1. 把图片存到BJSON中

  
  
  1. /// <summary>
  2. /// 把图片存到BJSON中
  3. /// </summary>
  4. public void SaveImgBJSON(byte[] byteImg)
  5. {
  6.     Document doc = new Document();
  7.     doc["ID"] = 1;
  8.     doc["Img"] = byteImg;
  9.     mongoCollection.Save(doc);
  10. }

2. 获取BJSON方式存储的图片字节数据

  
  
  1. /// <summary>
  2. /// 获取BJSON方式存储的图片字节数据
  3. /// </summary>
  4. public byte[] GetImgBJSON()
  5. {
  6.   Document doc=  mongoCollection.FindOne(new Document { { "ID", 1 } });
  7.   return doc["Img"] as Binary;
  8. }

上面两段代码是在对MongoDB相关操作进行BLL封装类中添加的两个方法,封装方式查看上节内容。下面看看在webform中如何调用:

在界面拖出一个FileUpload控件和一个Button控件,页面cs类加如下方法:

  
  
  1. protected void Button1_Click(object sender, EventArgs e)
  2. {
  3.     ImgBLL imgBll = new ImgBLL();
  4.     imgBll.DeleteAll();
  5.     imgBll.SaveImgBJSON(FileUpload1.FileBytes);
  6.     Response.BinaryWrite(imgBll.GetImgBJSON());
  7. }

二、用GridFS方式存取文件

在实现GridFS方式前我先讲讲它的原理,为什么可以存大文件。驱动首先会在当前数据库创建两个集合:"fs.files"和"fs.chunks"集合,前者记录了文件名,文件创建时间,文件类型等基本信息;后者分块存储了文件的二进制数据(并支持加密这些二进制数据)。分块的意思是把文件按照指定大小分割,然后存入多个文档中。"fs.files"怎么知道它对应的文件二进制数据在哪些块呢?那是因为在"fs.chunks"中有个"files_id"键,它对应"fs.files"的"_id"。"fs.chunks"还有一个键(int型)"n",它表明这些块的先后顺序。这两个集合名中的"fs"也是可以通过参数自定义的。

如果你只是想知道怎么用,可以忽略上面这段话,下面将用法:

1. GridFS方式的文件新建,读取,删除

  
  
  1. private string GridFsSave(byte[] byteFile)
  2. {
  3.     string filename = Guid.NewGuid().ToString();
  4.     //这里GridFile构造函数有个重载,bucket参数就是用来替换那个创建集合名中默认的"fs"的。
  5.     GridFile gridFile = new GridFile(mongoDatabase);
  6.     using (GridFileStream gridFileStream = gridFile.Create(filename))
  7.     {
  8.         gridFileStream.Write(byteFile, 0, byteFile.Length);
  9.     }
  10.     return filename;
  11. }
  12. private byte[] GridFsRead(string filename)
  13. {
  14.     GridFile gridFile = new GridFile(mongoDatabase);
  15.     GridFileStream gridFileStream = gridFile.OpenRead(filename);
  16.     byte[] bytes = new byte[gridFileStream.Length];
  17.     gridFileStream.Read(bytes, 0, bytes.Length);
  18.     return bytes;
  19. }
  20. private void GridFsDelete(string filename)
  21. {
  22.     GridFile gridFile = new GridFile(mongoDatabase);
  23.     gridFile.Delete(new Document("filename", filename));
  24. }

2. 再次封装GridFS操作,新文档只存储文件名称,相当于只是一个键,新文档还可以有除“文件名”之外其他的键。

  
  
  1. /// <summary>
  2. /// 把图片存到GridFS中
  3. /// </summary>
  4. public void SaveImgGridFS(byte[] byteImg)
  5. {
  6.     string filename = GridFsSave(byteImg);
  7.     Document doc = new Document();
  8.     doc["ID"] = 1;
  9.     doc["filename"] = filename;
  10.     mongoCollection.Save(doc);
  11. }
  12. /// <summary>
  13. /// 获取GridFS方式存储的图片
  14. /// </summary>
  15. public byte[] GetImgGridFS()
  16. {
  17.     Document doc = mongoCollection.FindOne(new Document { { "ID", 1 } });
  18.     string filename = doc["filename"].ToString();
  19.     return GridFsRead(filename);
  20. }
MongoDB中的索引其实类似于关系型数据库,都是为了提高查询和排序的效率的,并且实现原理也基本一致。由于集合中的键(字段)可以是普通数据类型,也可以是子文档。MongoDB可以在各种类型的键上创建索引。下面分别讲解各种类型的索引的创建,查询,以及索引的维护等。 一、创建索引 1. 默认索引 MongoDB有个默认的“_id”的键,他相当于“主键”的角色。集合创建后系统会自动创建一个索引在“_id”键上,它是默认索引,索引名叫“_id_”,是无法被删除的。我们可以通过以下方式查看:
   
   
  1. var _idIndex = mongoCollection.Metadata.Indexes.Single(x => x.Key == "_id_");
  2. Console.WriteLine(_idIndex);

2. 单列索引

在单个键上创建的索引就是单列索引,例如我们要在“UserInfo”集合上给“UserName”键创建一个单列索引,语法如下:(1表示正序,-1逆序)

   
   
  1. mongoCollection.Metadata.CreateIndex(new Document { { "UserName", 1 } }, false);

接着,我们用同样方法查找名为“_UserName_”的索引

   
   
  1. var _UserName_Index = mongoCollection.Metadata.Indexes.Single(x => x.Key == "_UserName_");
  2. Console.WriteLine(_UserName_Index);

3.组合索引

另外,我们还可以同时对多个键创建组合索引。如下代码创建了按照“UserId”正序,“UserName”逆序的组合索引:

   
   
  1. mongoCollection.Metadata.CreateIndex(new Document { { "UserId", 1 }, { "UserName", -1 } }, false);

4.子文档索引

我们可以对文档类型的键创建各种索引,例如单列索引,如下创建用户详细信息“Detail”的单列索引:

   
   
  1. mongoCollection.Metadata.CreateIndex(new Document { { "Detail", 1 } }, false);

对子文档的键创建组合索引:例如在“Detail.Address”和“Detail.Age”上创建组合索引:

   
   
  1. mongoCollection.Metadata.CreateIndex(new Document { { "Detail.Address", 1 }, { "Detail.Age", -1 } }, false);

5.唯一索引

唯一索引限制了对当前键添加值时,不能添加重复的信息。值得注意的是,当文档不存在指定键时,会被认为键值是“null”,所以“null”也会被认为是重复的,所以一般被作为唯一索引的键,最好都要有键值对。

对“UserId”创建唯一索引(这时候最后一个参数为“true”):

   
   
  1. mongoCollection.Metadata.CreateIndex(new Document { { "UserId", 1 } }, true);
二、维护索引 1. 查询索引 通过索引名查询的方式已有介绍。但有时候,我们可能忘记了索引名,怎么查询呢? 下面提供一个遍历全部索引的方法,打印全部索引信息:
    
    
  1. foreach (var index in mongoCollection.Metadata.Indexes)
  2.  {
  3.      Console.WriteLine(index.Value);
  4.  }

输出结果示例:

    
    
  1. { "name": "_id_", "ns": "myDatabase.UserInfo", "key": { "_id": 1 } }
  2. { "name": "_UserId_unique_", "ns": "myDatabase.UserInfo", "key": { "UserId": 1 }, "unique": true, "_id": "4d8f406ab8a4730b78000005" }
  3. { "name": "_UserName_", "ns": "myDatabase.UserInfo", "key": { "UserName": 1 }, "unique": false, "_id": "4d8f406ab8a4730b78000006" }
  4. { "name": "_Detail.Address_Detail.Age_", "ns": "myDatabase.UserInfo", "key": { "Detail.Address": 1, "Detail.Age": -1 }, "unique": false, "_id": "4d8f406ab8a4730b78000007" }
  5. { "name": "_UserId_UserName_", "ns": "myDatabase.UserInfo", "key": { "UserId": 1, "UserName": -1 }, "unique": false, "_id": "4d8f406ab8a4730b78000008" }
  6. { "name": "_Detail_", "ns": "myDatabase.UserInfo", "key": { "Detail": 1 }, "unique": false, "_id": "4d8f406ab8a4730b78000009" }

可见,集合的索引也是通过一个集合来维护的。name表示索引名,ns表示索引属于哪个库哪个集合,key表示索引在哪个键上,正序还是逆序,unique表示是否为唯一索引,等等...

2. 删除索引

新手常陷入的误区是,认为集合被删除,索引就不存在了。关系型数据库中,表被删除了,索引也不会存在。在MongoDB中不存在删除集合的说法,就算集合数据清空,索引都是还在的,要移除索引还需要手工删除。

例如,删除名为“_UserName_”的索引:

    
    
  1. mongoCollection.Metadata.DropIndex("_UserName_");

下面提供删除除默认索引外其他全部索引的方法:

    
    
  1. public void DropAllIndex()
  2. {
  3.     var listIndexes = mongoCollection.Metadata.Indexes.ToList();
  4.     for (int i = 0; i < listIndexes.Count; i++)
  5.     {
  6.         if (listIndexes[i].Key != "_id_")
  7.         {
  8.             mongoCollection.Metadata.DropIndex(listIndexes[i].Key);
  9.         }
  10.     }
  11. }
三、索引的效率 MongoDB的索引到底能不能提高查询效率呢?我们在这里通过一个例子来测试。比较同样的数据在无索引和有索引的情况下的查询速度。 首先,我们通过这样一个方法插入10W条数据:
     
     
  1. public void InsertBigData()
  2. {
  3.     var random = new Random();
  4.     for (int i = 1; i < 100000; i++)
  5.     {
  6.         Document doc = new Document();
  7.         doc["ID"] = i;
  8.         doc["Data"] = "data" + random.Next(100000);
  9.         mongoCollection.Save(doc);
  10.     }
  11.     Console.WriteLine("当前有" + mongoCollection.FindAll().Documents.Count() + "条数据");
  12. }

然后,实现一个方法用来创建索引:

     
     
  1. public void CreateIndexForData()
  2. {
  3.     mongoCollection.Metadata.CreateIndex(new Document { { "Data", 1 } }, false);
  4. }

还有排序的方法:

     
     
  1. public void SortForData()
  2. {
  3.     mongoCollection.FindAll().Sort(new Document { { "Data", 1 } });
  4. }

运行测试代码如下:

     
     
  1. static void Main(string[] args)
  2. {
  3.     IndexBLL indexBll = new IndexBLL();
  4.     indexBll.DropAllIndex();
  5.     indexBll.DeleteAll();
  6.     indexBll.InsertBigData();
  7.     Stopwatch watch1 = new Stopwatch();
  8.     watch1.Start();
  9.     for (int i = 0; i < 1; i++) indexBll.SortForData();
  10.     Console.WriteLine("无索引排序执行时间:" + watch1.Elapsed);
  11.     indexBll.CreateIndexForData();
  12.     Stopwatch watch2 = new Stopwatch();
  13.     watch2.Start();
  14.     for (int i = 0; i < 1; i++) indexBll.SortForData();
  15.     Console.WriteLine("有索引排序执行时间:" + watch2.Elapsed);
  16. }

最后执行程序查看结果:


多次测试表明在有索引的情况下,查询效率要高于无索引的效率。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值