说明:本文在个人博客地址为edwardesire.com,欢迎前来品尝。


  1. Mongoose学习

    这里的Mongoose当然不是图片上的萌物,它是一个MongoDB对象建模工具(object modeling tool),以前在sails上用的Waterline是ORM (Object Relational Mapper)。当使用Mongoose时,我们不在需要在数据库中创建好结构(Schema)之后,再与后端代码中创建的对象或类进行映射这样繁琐的操作。在Mongoose的封装下,我们只需定义好JSON类型的数据结构即可。当然我没有在Nodejs直接使用过MongoDB,不过想想一定也是很麻烦的。

    Mongoose的优点还有很多,我比较笼统地说一下。它实用性在于与数据库的交互是一种结构化以及可重复的方式,有助于进行一些很普遍的数据库任务,也减少了嵌套回调的复杂性。还有的是它不像MongoDB直接返回一个JSON的字符串,而是返回JSON对象。当然,目前Mongoose对于Schema-less data、Random documents、Pure Key-Value pairs是无解的。

    • 1.1 connection

      第一步当然是连接数据库了。如图,连接数据库的配置文大致分为三步。

      第一步是进行连接,连接字符串 mongodb://<db_user>:<db_password>@<hostname>:<port>/<dbname>中间必须填写的部分为server和hostname,我们可以使用两种方法来打开数据库连接(mongoose.connect和createConnection):我一般就使用mongoose.connect(db);,当我们需要使用多数据库连接时,我们就需要使用第二种方法了 var connectName = mongoose.createConnection(db#);第二步就是输出运行日志信息,在成功连接、断开连接或者报错时,监听相应的事件并在console输出运行信息;第三步是断开连接,一般的最佳实践是在程序运行时就打开连接,而程序停止或重启时就需要手动断开数据库连接。

    • 1.2 Schema Model

      Schema是一个文档的数据结构,正如我前面提到的,它在Mongoose是一个JSON对象。它最大的特点就是无需确定字段的大小,这特别适用于需要改变对象大小的情况。

      它支持8种数据类型(String、Number、Date、Boolean和Buffer、ObjectId、Mixed、ObjectId、Mixed、Array)。Buffer是用来存储2进制数据,ObjectId是不同于_id的特定的识别符。Mixed可以指定任意类型,不过Mongoose不会自动识别。Array用来存放基本数据类型,也可以是子文档。比如

        var childrenSchema = new Schema({
      //some structure
      });
      var fatherSchema = new Schema({
      //some structure
      children: [childrenSchema]
      });

      Model是对应Schema的编译版本,一个model的实例直接映射为数据库中的一个文档。基于这种关系,model处理所有的文档交互(也就是下文的CRUD)。我们通过 mongoose.model(modelname, schemaName)来构建model。这样一来我们就可以一鼓作气地将数据存入数据了。

        var mongoose = require('mongoose');
      var Schema = mongoose.Schema; //声明Schema
      var nodeSchema = new Schema({
      name: String,
      age: Number
      });
      //构建model
      mongoose.model('Node', nodeSchema);
      //简单的数据交互
      //创建两个实例
      var node = new Node({name: 'Edward', age: '23'});
      node.save(function(err){
      if(err){
      console.log(err);
      }else{
      console.log('The new node is saved');
      }
      });
    • 1.3 CRUD

      我们把Create、Read、Update、Delete操作一起称呼为CRUD,这4个操作是持久性存储的基本操作。在Mongoose中的模型方法(Model methods)对应的就有有Model.create(),Model.find(),Model.update(),Model.remove()方法,实例方法也是一样的,不过他作用于特定的实例罢了。

      • 1.3.1 Create Data

        首先是创建数据的模型方法 Model.create(),此方法直接将数据存入数据库。

          Node.create({name: 'Edward', age: '23'}, function(err, node, numAffected){
        if(err){
        res.send({'success':false,'err':err});
        }else{
        res.send({'success':true});
        console.log("node created and saved: " + node);
        res.redirect('/');
        }
        });

        而实例方法就是在创建实例就将数据以JSON对象传递给实例(如上一节的例子),当然我们也可以在实例创造之后再添加数据。

          var node = new Node();
        node.age = 23;

        但是这都只是保存在了应用,我们需要使用instance.save()保存。一步到位的写法如下。

          var node = new Node({name: 'Edward', age: '23'}).save(function(err){
        if(err){
        console.log(err);
        }else{
        console.log('The new node is saved');
        }
        });
      • 1.3.2 Read Data

        读取数据,模型方法有3种:Model.find()---找到所有符合添加的文档并返回一个表单, Model.findOne()---返回首先找到的单个文档,Model.findById()---通过ID(唯一)来查找。这3属于静态方法,我们也可以创建自己的静态方法。比如通过文档中的某个键来查找数据。

          Dtree.findByName(req.params.name, function(err, dtree){
        if(!err){
        //do something
        }else{
        console.log('Somthing wrong: ' + err);
        }
        });

        这些方法的完整参数为 Model.find(conditions, [fields], [options], [callback]),可选项fields为指定返回的值,options为指定序列等。具体的细节可以看文档MongooseAPI。需要注意的是,如果不定义回调函数的话,需要使用.exec()来显性调用更新函数。

      • 1.3.3 Update Data

        更新数据同样有3个静态模型方法:Model.update(),Model.findOneAndUpdate(),Model.findByIdAndUpdate()。他们的参数都有4个(conditions, update, ooptions,callback)。同样在文档MongooseAPI中可以查询到。

        但是这3种方法都无法使用一些自定义的运行机制。而这有一套标准做法:find-edit-save方法。我们来看看例子。

          //1.查找记录
        Dtree.findByName(req.params.name, function(err, dtree){
        if(!err){
        //成功读取dtree
        //读取JSON文件,获得需要添加的内容
        var json;
        fs.readFile('./public/javascripts/update.json', 'utf8', function (err, data) {
        if(err) throw err;
        json = JSON.parse(data);
        //2.修改dtree记录,将json插入到structure
        dtree.structure.push(json);
        //3.保存记录到数据库
        dtree.save(function(err, tree){
        if(err){
        console.log('Somthing wrong: ' + err);
        }else{
        console.log('Add a new node', tree);
        res.redirect('/dtree/json/Type00');
        }
        });
        });
        }else{
        console.log('Somthing wrong: ' + err);
        }
        });
      • 1.3.4 Delete Data

        删除数据同样需要查找到数据再删除:Model.remove(),Model.findOneAndRemove(),Model.findByIdAndRemove()。.remvoe()的参数就是可选择的callback,后面两个还多了一个option参数,具体可查询Mongoose API。而.remove()方法可以作为模型方法调用,也可以作为实例方法调用。

          //Model method
        Node.remove({name: 'Edward'} function(err){
        if(!err){
        //成功删除所有name为Edward的文档
        }
        }); //Instance method
        Node.findOne({name: 'Edward'}, function(err, node){
        if(!err){
        node.remove(function(err){
        //成功删除首位name为Edward的文档
        });
        }
        });

    好了,Mongoose的学习就暂时告一段落,接下来说说项目遇到的问题。


  1. 项目实战

    前端传到后台的内容为一个json结构的决策树,大致的结构如下。分为三大部分:config,parameter,structure。

    前面两部分相对比较容易解决,最大的问题是structure中有个children子节点,而子节点还会增加新的子节点,具体层级也是随着问题变化的。在我的第一版Schema中很天真地这样定义。

     var dtreeSchema = new Schema{
    //其他数据结构
    structure: [chilldrenSchema]
    };
    var chilldrenSchema = new Schema{
    //其他数据结构
    children: [chilldrenSchema]
    };

    程序的控制是这样写的

     //params req.params.name
    exports.createDtreeChildren = function(req, res){
    //Find dtree by name
    Dtree.findByName(req.params.name, function(err, dtree){
    if(!err){
    //成功读取tree
    //读取新增结点
    var json;
    fs.readFile('./public/javascripts/update.json', 'utf8', function (err, data) {
    if(err)throw err;
    json = JSON.parse(data);
    //structure parse
    //structure 为一个数组
    //structure[i] 为首个结点
    //structure[i].children 为其子节点
    var newchildren = dtree.structure[0].children;
    //2.插入structure
    dtree.structure[0].children.push(json);
    console.log(dtree.structure[0].children);
    dtree.markModified(dtree.structure[0].children);
    //3.save to mongodb
    dtree.save(function(err, tree){
    if(err){
    console.log('Somthing wrong: ' + err);
    }else{
    console.log('Add a new node: '+ dtree.structure[0].children);
    res.redirect('/dtree/json/Type00');
    }
    });
    });
    }else{
    console.log('Somthing wrong: ' + err);
    }
    });
    };

    这样会出现一个问题,那就是输出(dtree.structure[0].children)的是正确修改后的数据,而却没有正确存入数据库。其中的原因是Mogoose对于结构的声明是有严格顺序的(Order of schema declarations)。同样的,我在第二次修改后,chilldrenSchema写到了dtreeSchema的前面,chilldrenSchema自己的children的[chilldrenSchema]类型如期望一样无法存入数据库,Mongoose把undefined(具体是[undefined]还是undefined我不确定)。我想到了一个十分丑陋的解决方法就是手动地添加足够大的层数。

     var ninethChilldrenSchema = new Schema{
    //其他数据结构
    //children: [nextLaryerChilldrenSchema]
    };
    var eighthChilldrenSchema = new Schema{
    //其他数据结构
    children: [ninethChilldrenSchema]
    };
    //中间依次类推到底
    var chilldrenSchema = new Schema{
    //其他数据结构
    children: [secondChilldrenSchema]
    };
    var dtreeSchema = new Schema{
    //其他数据结构
    structure: [chilldrenSchema]
    };

    这种类似于俄罗斯套娃结构的方法能解决一部分问题,但是无法适应真实应用环境。因为决策树的层数是可大可小的,也无法预估一个合适的最大值,况且代码也不美观。这个问题也一直悬在这里,希望有大神能够留下联系方式和解决方法,予人玫瑰,手留余香。而项目因为时间关系,估计就只能修改结构来逃避这个问题了。

  2. Next

    选择的替代方案是将这种树状结构变成简单的数组结构,然后在后端与前端交互时进行树结构的拼接和拆散。这种方法涉及到树与二叉树的转化以及二叉树的序列化两方面知识。好好学习

Mongoose:Schema之路的更多相关文章

  1. Mongodb学习(1)--- mongoose: Schema, Model, Entity

    Schema : 一种以文件形式存储的数据库模型骨架,不具备数据库的操作能力 Model : 由 Schema 发布生成的模型,具有抽象属性和行为的数据库操作 Entity : 由 Model 创建的 ...

  2. [js高手之路]Node.js+jade+mongodb+mongoose实现爬虫分离入库与生成静态文件

    接着这篇文章[js高手之路]Node.js+jade抓取博客所有文章生成静态html文件继续,在这篇文章中实现了采集与静态文件的生成,在实际的采集项目中, 应该是先入库再选择性的生成静态文件.那么我选 ...

  3. [js高手之路]Node.js+jade+express+mongodb+mongoose+promise实现todolist

    promise主要是用来解决异步回调问题,其实还有好几种比promise更好的方案,后面再说,这节,我们先用promise来改造下,我以前写的一篇文章[js高手之路]javascript腾讯面试题学习 ...

  4. [译]Mongoose指南 - Schema

    定义schema 用mongoose的第一件事情就应该是定义schema. schema是什么呢? 它类似于关系数据库的表结构. var mongoose = require('mongoose'); ...

  5. [原译]在mongoose中对Array Schema进行增删改

    原文地址: http://tech-blog.maddyzone.com/node/add-update-delete-object-array-schema-mongoosemongodb 本文为上 ...

  6. 85.Mongoose指南 - Schema

    转自:https://www.bbsmax.com/A/pRdBnKpPdn/ 定义schema 用mongoose的第一件事情就应该是定义schema. schema是什么呢? 它类似于关系数据库的 ...

  7. Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API

    继续开扒我们的MEAN栈开发之路,前面两节我们学习了Express.Jade引擎并创建了几个静态页面,最后通过Heroku部署了应用. Nodejs之MEAN栈开发(一)---- 路由与控制器 Nod ...

  8. NodeJS实战:Express+Mongoose+ejs

    元宵还没到,先向所有朋友拜一个晚年~~~ 文章目录: 1.组件版本号 -- --node -- --express -- --Mongoose 2.初始化项目 firstblood -- --用 ex ...

  9. 在express项目中有效组织和使用mongoose

    平凡之路 1.创建express项目 express mongooseExpress 2.最简express var express = require("express"); v ...

随机推荐

  1. javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint

    使用hibernate validator出现上面的错误, 需要 注意 @NotNull 和 @NotEmpty  和@NotBlank 区别 @NotEmpty 用在集合类上面@NotBlank 用 ...

  2. tcpdump抓SQL

    前言:假设如果有个服务器几十个链接突然达到上千个链接,show processlist,general_log,还有慢查询日志这些都不能用,你怎么把这些链接过来的SQL情况了解清楚,如果你觉得那些好用 ...

  3. Atheros AR9485 ubuntu 10.04 驱动安装及networking disable问题解决

    Laptop: ACER Aspire 5733-6629 Wireless:Lite-on HB125, CHIPS: Atheros AR9485 Ubuntu: 10.04LTS (2.6.32 ...

  4. 转:Cache相关

    声明:本文截取自http://blog.163.com/ac_victory/blog/static/1033187262010325113928577/ (1)“Cache”是什么 Cache(即高 ...

  5. POJ 1753 Flip Game (高斯消元 枚举自由变元求最小步数)

    题目链接 题意:4*4的黑白棋,求把棋全变白或者全变黑的最小步数. 分析:以前用状态压缩做过. 和上题差不多,唯一的不同是这个终态是黑棋或者白棋, 但是只需要把给的初态做不同的两次处理就行了. 感觉现 ...

  6. OpenSSL再爆多处高危漏洞

    OpenSSL团队于北京时间6月5号晚8点左右发布了5个安全补丁,这次的更新涉及多处高危漏洞,连接:http://www.openssl.org/news/ 受影响的版本包括: OpenSSL 1.0 ...

  7. gradle command not found

    find / -name 'gradle*' .... /Applications/Android Studio.app/Contents/gradle/gradle-2.10/bin/gradle ...

  8. [反汇编练习] 160个CrackMe之002

    [反汇编练习] 160个CrackMe之002. 本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注 ...

  9. LeetCode Triangle 三角形(最短路)

    题意:给一个用序列堆成的三角形,第n层的元素个数为n,从顶往下,每个元素可以选择与自己最近的两个下层元素往下走,类似一棵二叉树,求最短路. [], [,4], [6,,7], [4,,8,3] 注意: ...

  10. LeetCode Single Number II 单元素2

    题意:给一个序列,其中只有1个元素只出现1次,其他的都一定出现3次.问这个出现一次的元素是多少? 思路: (1)全部元素拆成二进制,那么每个位上的1的个数应该是3的倍数,如果不是3的倍数,则ans的这 ...