美文网首页vpsmongodb
Mongoose教程翻译

Mongoose教程翻译

作者: 前端幼儿班 | 来源:发表于2017-09-04 15:02 被阅读299次

    原文地址

    本文简单的介绍了数据库,以及如何在 Node/Express 中应用他们。之后展示如何使用Mongoose为一个图书网站提供数据访问。介绍了Mongoose的模式和模型的声明,主要属性类型,基础校验器。也展示了访问数据模型的几种主要方法。

    概括


    图书管理员将使用一个 图书管理网站 去记录书籍和借书人的信息,同时读者会使用 图书管理网站 去浏览搜索图书,去查看是不是某书全部借出,然后预约或直接借走该书。为了更有效率存储信息,这里我们需要一个数据库。

    Express提供了多种数据库的支持,你有多种方式去实现增删查改操作。本文简要的概述了一些可用的选项,并详细说明一些查询的特殊机制。

    使用什么数据库?

    Express应用可以使用Node支持的各种数据库(Express本身对数据库并没有特殊要求)。常用的选项包括PostgreSQL,MySQL,Redis,SQLite,MongoDB。

    当选择一个数据库时,我们需要考虑的常常有 时间成本,学习曲线,性能,备份和回滚的易用性,成本以及其社区支持情况。虽然并没有一个最好的数据库,但是对于我们的 图书管理网站 这样的小型网站,任何流行的数据库都是可以的。

    如何更好的与数据库交互?

    与数据库交互有两种方法:

    1. 使用数据库的自带的查询语言(比如SQL)
    2. 使用对象数据模型("ODM")或对象关系模型 ("ORM")。一个ODM或ORM对象代表的就是一个映射到底层数据库的数据对象比如说JSON对象。一些ORM对象是指定数据库的,一些则不然。

    使用SQL语言或者其他数据库支持的语言可以获得很好的性能。ODM则相对比较慢,因为需要代码去转换映射的对象和数据库中的格式,所以他生成的查询语句可能不够高效(尤其是在ODM为了支持不同的数据库后台,这时必须对数据库功能做出极大的妥协)。

    使用ODM的优势在于程序员可以一直关注与JavaScript 对象而不是数据库语义,尤其是在你需要和不同的数据库交互(可能是同一应用,或不同应用)。ODM也提供了清晰方式去校验检查数据。

    使用ODM或ORM可以降低开发和维护成本,除非你非常擅长原生查询语言,或对性能要求很高,否则你都应该优先考虑使用ODM或ORM。

    使用什么ODM/ORM

    在npm中有许多ODM、ORM。

    在本文写作时几个热门的框架

    • Mongoose:Mongoose是一个用于异步环境的MongoDB的对象模型。
    • Waterline:提取自基于Express的Sail框架的对象关系模型。他为众多数据库提供了统一的API接口,包括 Redis, mySQL, LDAP, MongoDB, 和 Postgres。
    • Bookshelf:同时具备promise和传统回调函数的接口,提供了对事务的支持,eager/nested-eager relation loading(不知道咋翻),集成多态,支持 一对一,一对多,多对多关系。支持PostgreSQL, MySQL, 和 SQLite3。
    • Objection:尽可能的简化的使用数据库和SQL的全部功能(支持SQLite3, Postgres 和 MySQL)
    • Sequelize:基于promise的ORM...

    在选择解决方案时一般应该考虑他们都提供哪些功能,以及他们社区的活跃度(下载,捐款,Bug报告,文档质量)。在此文写作时Mongoose当前最受欢迎的ORM,如果你在你的应用中使用MongoDB作为你的数据库,那么他是一个合理的选择。

    在LocalLibrary(这篇文章中的项目名)使用MongoDB和Mongoose

    在本文中我们使用Mongoose来访问我们的图书数据。Mongoose作为MongoDB的前端,MongoDB是一个开源的NoSQL,使用面向文档的对象模型的数据库。在MongoDB中 集合(collection)中的文档(documents)类似于关系数据库中的表(table)中的行(row)。

    这对ODM和数据库组合在Node的社区中是非常流行的,部分原因是因为文档存储和查询起来非常类似于JSON,对于JS程序员这是非常熟悉的。

    你不必为了使用Mongoose而去了解MongoDB,但是如果你已经了解MongoDB,可以更容易的使用和理解Mongoose

    教程后续部分将讲解如何为LocalLibrary定义以及使用Mongoose模板和模型。

    LocalLibrary的model设计


    当你开始进行model编码的时候,花一些时间考虑你应该需要存储什么数据,以及不同对象之间的关系。

    我们知道我们需要存储有关书籍的信息(书名,概要,作者,类型,书号),而且一种书我们可能有多本(拥有唯一的ID,可用状态),我们可能也需要存储除了姓名之外其他的作者信息,而且可能会有多个作者名是相同或相似的。我们还想能够通过 书名,作者,书的类别进行排序。

    当你设计你的Model时为不同的Object(拥有一组相关的信息的对象)设置不同的Model是必要的。在当前的实例中明显的对象有 书籍,书籍实体,作者。

    你可能也想为一个下拉列表选项新建一个Model。相对于硬编码,当下拉列表不确定或经常更改,这种方式更加推荐。在本例中书籍类型(科幻小说,法国诗歌)明显就是属于这种类型。

    一旦我们确定了Model和相应的属性,我们就需要思考他们之间的关系。

    考虑到这一点,下面的UML关系图展示了当前我们定义的Model。根据上文的讨论,我们将为书籍(包含书籍的一般信息),书籍实体(包含这本书在系统中的状态),作者 创建Model。同时我们也决定为书籍的类别创建一个Model,这样书的类别就可以动态修改。书籍实体的状态并不常变化,我们不会为他单独创建Model。在每个方块中我们定义了Model的名字,以及属性名和类型,还有方法名和方法返回值的类型。

    该图也展示了Model直接的关系,和他们数量的对应关系(最大和最小)。比如在Book和Genre之间的连线,在靠近Book的数字表示一本书有0或者多个Genre,而另一端的数字表示每个Genre有0或者多个Book

    天杀的UML图

    Mongoose入门


    这部分概括了如何用Mongoose连接MongoDB,如何定义Schema(这个之前被我翻成模板,不知道对不对,后面索性不返了) 和Model,如何进行简单的查询。

    安装Mongoose和MongoDB

    Mongoose和其他的依赖一样被安装,使用以下命令为你的项目安装Mongoose。

    npm install mongoose --save
    

    安装Mongoose会自动加入他的依赖,例如MongoDB的驱动,但并没有安装MongoDB数据库本身。如果你想要安装MongoDB你可以从这里下载多个平台的安装包。你也可以使用云端的MongoDB实例。

    提示:在本文中我们将使用mLab的云端作为数据库。这样非常适合开发,对于这个教程这样做也是有意义的,因为他使安装过程不依赖读者的操作系统。

    连接到MongoDB

    Mongoose会向MongoDB请求连接。你能够使用require()引入Mongoose,并使用mongoose.connect()连接到本地数据库,如下所示:

    //Import the mongoose module
    var mongoose = require('mongoose');
    
    //Set up default mongoose connection
    var mongoDB = 'mongodb://127.0.0.1/my_database';
    mongoose.connect(mongoDB);
    
    //Get the default connection
    var db = mongoose.connection;
    
    //Bind connection to error event (to get notification of connection errors)
    db.on('error', console.error.bind(console, 'MongoDB connection error:'));
    

    用mongoose.connection你可以获得Mongoose的默认Connection 对象。一旦连接完成,open 事件将从Connection 实例中发射。

    提示:如果你需要额外的Connection,你可以使用mongoose.createConnection(),他接受一个和connect()相同格式的数据库URI(包含host,数据库名,端口,选项),返回一个Connection 实例。

    定义并创建Model

    Model是使用Schema 接口来定义。Schema 用来定义存储在document 中的属性,并且赋予他们校验的规则,和默认值。另外你可以定义静态 或 实体 的helper方法,使你的数据类型更加易于使用。你也可以定义像其他属性一样使用的虚拟属性,这些属性并不会被保存到数据库中(这些会在后文中讲解)。

    Schema使用mongoose.model()去“编译”入Model。一旦拥有一个model ,你可以使用他用来创建,查询,删除指定的对象。

    Model对应的是MongoDB中documents的collection,documents中包含在Schema中的定义的属性和属性对应的类型。

    定义Schema

    下面的代码展示了如何定义一个简单的Schema。首先引入mongoose,然后使用Schema的构造方法新建一个Schema的实例,在构造函数的参数对象中定义属性。

    //Require Mongoose
    var mongoose = require('mongoose');
    
    //Define a schema
    var Schema = mongoose.Schema;
    
    var SomeModelSchema = new Schema({
        a_string: String,
        a_date: Date
    });
    

    在上面的例子中,我们只有两个属性,一个字符串,一个时间。在本文的下一段我们会展示其他属性类型,和校验器和其他方法。

    创建一个Model

    Model是使用mongoose.model()创建自Schema。

    // Define schema
    var Schema = mongoose.Schema;
    
    var SomeModelSchema = new Schema({
        a_string: String,
        a_date: Date
    });
    
    // Compile model from schema
    var SomeModel = mongoose.model('SomeModel', SomeModelSchema );
    

    第一个参数是MongoDB中的集合的名,mongoose将为上面的Model创建一个名为SomeModel的集合。第二个参数是你想要用来创建Model的Schema 。

    一旦你创建了Model,你可以使用他来进行增删查改,既可以查询全部记录,也可以查询特定的子集。当我们创建我们的视图时,我们会在“使用Model”段讲解如何做。

    Schema 中属性的类型

    Schema 可以有任意数量的属性,每一个属性都代表了在MongoDB中的字段。下面的例子展示了常用的属性类型是如何被定义的。

    var schema = new Schema(
    {
      name: String,
      binary: Buffer,
      living: Boolean,
      updated: { type: Date, default: Date.now },
      age: { type: Number, min: 18, max: 65, required: true },
      mixed: Schema.Types.Mixed,
      _someId: Schema.Types.ObjectId,
      array: [],
      ofString: [String], // You can also have an array of each of the other types too.
      nested: { stuff: { type: String, lowercase: true, trim: true } }
    })
    

    大多数属性类型的意义是显而易见的,除了以下几项:

    • ObjectId:代表在数据库中的一个对象实体,比如,书本对象可以使用他来代表他的作者。实际上他包含的是对象的唯一id(_id)。我们可以在需要的时候使用populate()方法获取某些信息。
    • Mixed:任意schema类型。
    • []:数组对象。你可以对该对象执行JavaScript数组操作(push, pop, unshift等)。上面的实例展示了,没有指定数组对象类型的数组,和指定为String的数组。你可以指定任意类型的数组。

    这段代码也展示来定义属性的两种方法:

    • 属性名和属性类型作为键值对。
    • 属性名后紧跟一个对象来定义属性类型,以及属性的其他选项。选项包含以下这些:
      • 默认值
      • 内置的校验器(min/max),或者定制的校验函数。
      • 属性是否是必须的。
      • 属性是否会自动大写,小写或者去除空格(e.g. { type: String, lowercase: true, trim: true })。
        更多有关选项的信息,请看SchemaTypes的文档。
    校验器

    Mongoose提供来内置的校验器,自定义校验器,同步或者异步校验器。他用来指定可用的范围或者值,以及在校验失败时的错误信息。

    内置的校验器包括:

    • 所有的类型都包含required校验器。这个校验器是用来指定在保存时,属性是否是必须的。
    • Numbersminmax校验器。
    • Strings类型有:

    下面的样例略微修改自Mongoose的文档,展示来如何指定校验器的类型和错误信息。

    var breakfastSchema = new Schema({
          eggs: {
            type: Number,
            min: [6, 'Too few eggs'],
            max: 12
            required: [true, 'Why no bacon?']
          },
          drink: {
            type: String,
            enum: ['Coffee', 'Tea', 'Water',]
          }
        });
    

    详细的校验器说明,请看Mongoose的文档Validation

    虚拟属性

    虚拟属性是你可以get和set的对象属性,但是他们不会被保存到MongoDB中。get方法常常被用来格式化或者合并属性,set方法常用来分解单个属性并把他们保存在数据库中的多个属性中。在本例中用first name和last name属性去构造一个全名,相对于每次在使用时来构造一个全名更加清晰和简单。

    备注:我们将使用虚拟属性为每条记录的_id属性和地址定义一个唯一URL。
    更多信息请看Virtuals

    方法和查询助手

    schema可以有实体方法,静态方法和查询助手。实体方法和静态方法是类似的,他们之间明显的不同是,实例方法是关联到实际对象的,能够访问当前对象。查询助手允许你扩展mongoose的查询构造器API(比如,你可以添加“byName”查询方法去扩展find(), findOne() 和 findById())。

    Model的使用

    一旦你创建创建了schema,你就可以使用他来创建Model。Model代表了数据库中Document的Collection,而一个Model的实体代表了一个你可以存取的单一对象。
    下面我们提供一个概述,详情请看Models

    创建和更改document

    你可以通过创建一个Model实体并调用save()方法去保存一条记录。下面的例子假设SomeModel是通过schema创建的某个对象(只有一个“name”属性)。

    // Create an instance of model SomeModel
    var awesome_instance = new SomeModel({ name: 'awesome' });
    
    // Save the new model instance, passing a callback
    awesome_instance.save(function (err) {
      if (err) return handleError(err);
      // saved!
    });
    

    注意记录的创建(以及更新,删除,查询)是异步操作,你需要传递一个回调函数,当操作完成时会执行。我们遵从错误优先的惯例,所以回调函数的第一个参数为错误信息,如果有的话。如果操作会返回结果,他将被作为第二个参数。

    你也可以使用create()方法,在你定义对象的同时保存他。回调函数将返回错误信息作为第一个参数,创建的实体作为第二个参数。

    SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
      if (err) return handleError(err);
      // saved!
    });
    

    每一个Model都有一个相关的连接对象(当你使用model()方法时,会使用默认方法),你可以创建一个新的连接,并调用他的model()方法,用以在不同的数据库中创建记录。

    你可以使用点语法去访问对象属性,更改属性值。你必须使用save()或update()将变更保存到数据库中。

    // Access model field values using dot notation
    console.log(awesome_instance.name); //should log 'also_awesome'
    
    // Change record by modifying the fields, then calling save().
    awesome_instance.name="New cool name";
    awesome_instance.save(function (err) {
       if (err) return handleError(err); // saved!
       });
    
    搜索数据

    你可以通过查询方法去检索数据记录,并用JSON对象来指定查询条件。下面的代码展示了如何查询所有参加网球运动的运动员,并只返回姓名和年龄。这里我们只匹配了运动这一个属性,但是你们可以指定更多的检索条件,如一个正则表达式,或者不要任何条件,返回所有数据。

    var Athlete = mongoose.model('Athlete', yourSchema);
    
    // find all athletes who play tennis, selecting the 'name' and 'age' fields
    Athlete.find({ 'sport': 'Tennis' }, 'name age', function (err, athletes) {
      if (err) return handleError(err);
      // 'athletes' contains the list of athletes that match the criteria.
    })
    

    如果你向上面一样指定了回调方法,查询会马上执行,而回调方法会在查询完成后执行。

    在mongoose中所有回调函数都采用了callback(error, result)的形式。如果在查询时发生错误,error中将包含错误信息,而result将返回null。如果查询成功error是null,而result中包含查询的结果。

    如果你没有传递回调方法,程序将返回一个Query对象。你可以使用这个query对象去组建你的查询,之后调用exec()方法执行他,并传入回调方法。

    // find all athletes that play tennis
    var query = Athlete.find({ 'sport': 'Tennis' });
    
    // selecting the 'name' and 'age' fields
    query.select('name age');
    
    // limit our results to 5 items
    query.limit(5);
    
    // sort by age
    query.sort({ age: -1 });
    
    // execute the query at a later time
    query.exec(function (err, athletes) {
      if (err) return handleError(err);
      // athletes contains an ordered list of 5 athletes who play Tennis
    })
    

    上面的代码中,我们在find中指定了查询条件。我们也可以使用where()方法,他能够使用(.)点语法将所有查询条件连接起来,而不用分别指定。下面的代码等同于上面的代码,但是我们添加了一个age查询条件。

    Athlete.
      find().
      where('sport').equals('Tennis').
      where('age').gt(17).lt(50).  //Additional where query
      limit(5).
      sort({ age: -1 }).
      select('name age').
      exec(callback); // where callback is the name of our callback function.
    

    find()方法会查询所有匹配的记录,但是通常我们只需要其中的一条。下面的方法用以查询一条记录:

    提示:也有count()方法,获取指定条件的记录数。常常用于,你只想要知道数目而不是实际的记录时。

    查询中你还可以作很多,详情请看:Queries

    处理相关的对象 -----热门

    你可以使用ObjectId 属性类型创建一个索引连接两个对象,或者使用ObjectId 的数组去连接多个对象。这个属性存储着model的id。如果你需要关联对象的实际内容,你可以使用populate()方法去查询并替换id为真实数据。
    例如,一下的schema定义了作者和故事。每个作者有多个故事,我们将使用ObjectId 数组来表示他们。一个故事只有一个作者。"ref"属性(高亮加粗显示的,makedown)告诉schema 连接哪个model。

    var mongoose = require('mongoose')
      , Schema = mongoose.Schema
    
    var authorSchema = Schema({
      name    : String,
      stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
    });
    
    var storySchema = Schema({
      author : { type: Schema.Types.ObjectId, ref: 'Author' },
      title    : String,
    });
    
    var Story  = mongoose.model('Story', storySchema);
    var Author = mongoose.model('Author', authorSchema);
    

    我们可以使用_id值去保存关联对象的索引。下面我们创建一个author,之后是一个book对象,并关联author对象到author属性。

    var bob = new Author({ name: 'Bob Smith' });
    
    bob.save(function (err) {
      if (err) return handleError(err);
    
      //Bob now exists, so lets create a story
      var story = new Story({
        title: "Bob goes sledding",
        author: bob._id    // assign the _id from the our author Bob. This ID is created by default!
      });
    
      story.save(function (err) {
        if (err) return handleError(err);
        // Bob now has his story
      });
    });
    

    我们的story对象依靠id获得了author的索引。为了获得详细的author信息我们使用populate()方法。如下:

    Story
    .findOne({ title: 'Bob goes sledding' })
    .populate('author') //This populates the author id with actual author information!
    .exec(function (err, story) {
      if (err) return handleError(err);
      console.log('The author is %s', story.author.name);
      // prints "The author is Bob Smith"
    });
    

    血条耗尽。。。。

    相关文章

      网友评论

      • chen_f101:楼主 你好,可否帮忙看下【TypeError: mongoose.connect is not a function】这个问题,新手尝试在vue里面使用mongoose来连接mongoDB;

        使用的版本:"mongoose": "5.3.8"

        页面JS部分:
        var mongoose = require('mongoose')
        mongoose.connect('mongodb://localhost/test')
        var db = mongoose.connection
        db.on('error', console.error.bind(console, 'connection error:'))
        db.once('open', function() {
        // we're connected!
        })

        编译不报错,但是页面会提示
        [Vue warn]: Error in created hook: "TypeError: mongoose.connect is not a function"

      本文标题:Mongoose教程翻译

      本文链接:https://www.haomeiwen.com/subject/vtrddxtx.html