zoukankan      html  css  js  c++  java
  • 细嚼慢咽 Mongoose 5

    此文已由作者黄锴授权网易云社区发布。

    欢迎访问网易云社区,了解更多网易技术产品运营经验。

    前言

    由于Mongoose一直没有中文文档,加上现在市面上充斥着太多“快速上手”,很多中文文档都只盲目介绍了Mongoose的API用法(但是现在升级后,很多API发生了变化),没有做详细介绍,导致很多人用了半天的Mongoose,还是对其一知半解(比如我!),因此写了这篇文章,从以下三个角度聊聊Mongoose。

    • 是什么(what?)

    • 为什么?(why?)

    • 怎么用(how?)

      • 快速上手

      • 再深一点(这才是细嚼慢咽的关键哦)

    本文阅读可快,可慢全凭喜好。但本着贪多嚼不烂的想法,还是希望客观慢慢品尝。

    是什么(what)?


    首先,必须要知道Mongoose是什么

    Mongoose(狐獴)就是图上这个小可爱。(学名:Suricata suricatta),头尾长42-60厘米,是一种小型的哺乳动物……

    好像有点跑题。当然,今天介绍的Mongoose不是真的小动物,而是一款在Node.js环境下对MongoDB API 进行便捷操作的对象模型工具(所以理论上,我默认你是需要大概懂MongoDB数据库才会来用这个的)。

    要特别强调的是,我这里说它是对象模型工具,说明Mongoose的操作是以对象为单位的。

    同时,由于Mongoose升级后(差不多)全面支持Promise,可以很方便的对接async/await语法。因此接下来的我的代码都会默认使用Promise形式。

    为什么(Why)?

    我们知道,MongoDB官网提供了一个Node.js环境下的MongoAPI,那为什么我还需要使用Mongoose呢?当然是因为它提供了一些官方不能提供的功能,如它官网介绍的:

    Let's face it, writing MongoDB validation, casting and business logic boilerplate is a drag.

    可以看出,Mongoose主要是减轻我们在写数据验证,业务逻辑模板的负担。同时,由于Mongoose一直强调它是一个MongoDB对象建模工具,因此在这里就必须要提出Mongoose的核心,也是Mongoose困扰人已久的一个东西——模式和模型。

    模式?模型?

    对于Mongoose的新人来说,最头疼的就是它里面有关模式(Schema),模型(Model)的概念。Mongoose的一切都始于一个模式。这点和MongoDB基于集合(Collection)的思路可能不太一致,因为本身MongoDB是无模式的,也正是因为它的无模型,导致其使用起来极具灵活性。

    那Mongoose为什么要定义一个模型?Why Define a Mongoose Schema? 给出了解释:

    have you ever wondered how to pull these random documents out and figure out which properties were saved to document?  The truth is that for 90% of your web applications you’ll be using model classes and you’ll have a standard set of fields you’ll be expecting to be saved to your database

    模式有模式的好处,生活中绝大部分的数据是遵循一定的模式的,模式能让我们规范数据的表现形式,不至于太随心所欲。同时模式给我们提供了很多数据验证的便利(这就像我们讨论JavaScript弱类型和TypeScript强类型是一样一样的)。

    同时,由于Mongoose使用的依然MongoDB而非关系型数据库,因此它没有完全失去其天生的灵活性,它依然开放了让你可以定义任意字段的能力。

    // Defining a schema on a Mongoose model// that allows storage of arbitrary fields// Mongoose.schema(schemaObject, options)var User = Mongoose.schema({
      username: String,
      password: String}, {
      strict: false});

    注意到上面代码的第二个字段,你把strict设置为false,你就能在这个模型中加入任意字段了(但是还是不建议这样做,因为这意味着你需要自己去做数据的验证)。

    如果你实在不习惯使用 模式,可以使用MongoDB官方的API。

    与其说Mongoose定义模式是多此一举的,不如说,它在灵活性的基础上,提供了更安全的模型化操作对象的方式。

    我个人感觉:使用Mongoose你更有操作对象的感觉。

    模式 VS 模型

    我们已经知道了模式是有其优势的,那为什么我还需要模型?这两个有什么区别?

    官方的定义:Each schema defines the shape of the documents within that collection.

    模式

    模式它是一个 抽象的概念。其实可以把模式考虑成设计规范 ,定义了设计图纸应该如何画,默认单位是多少,如果没有该规范,那每个人画的设计图纸就是各式各样,没法真正投入建造。


    模型

    模型相对于模式是具体的概念,可以把模型考虑成设计图纸 :按照设计规范画出一个图纸,任何人根据该规范就可以把房子造出来。

    实例

    而模型的实例才是真正的我们住的房子,在MongoDB里也就是文档(document)


    多此一举?

    你可能会说,何必多此一举,把模式和模型合起来不就好了嘛?很多人喜欢这样写:

    const studentModel = Mongoose.model('Student', new Mongoose.Schema({ name: String, sex: String});

    这样写也可以,但是这个前提是你确定你的模式只在这一个模型中使用。之所以有个模式这个抽象概念,就像设计规范, 它从来不是说只能在一张图纸中使用,你可以利用这个规范绘制出很多符合规范的图纸。

    有时候你可能会有这样的需求,基本一样的数据结构,但是我需要建多个不同集合,或者稍微修改一下模式,新建一个新的集合,这时候把模式独立出来,你就会发现省事很多了。

    const personSchema = new Mongoose.Schema({ name: String, sex: String});const studentModel = Mongoose.model('Student', personSchema);const otherStudentModel = Mongoose.model('OtherStudent', personSchema);const teacherSchema = personSchema.clone();
    teacherSchema.add({ teach: String});const teacherModel = Mongoose.model('Teacher', teacherSchema);

    举例

    再来举一个例子:假设,我们建立了一个宠物猫咪乐园,我们需要去招集一批宠物喵成为我们的会员。但是呢,我们的宣传员他没见过喵(多么神奇的生物)。于是,我们绞尽脑汁,合计出一个规范:所有的宠物猫应该满足三个属性name,type, meow。也就是说,宠物喵必须要有名字,必须有一个品种,同时它必须要会“喵喵”叫!

    const catSchema = new Mongoose.Schema({ name: String, type: String,meow: Boolean});

    好了,现在我们把规范告诉他,但是他说他记性不好。怕忘记,因此我们制定了一个叫《宠物猫咪指南1.0》的手册

    const CatGuide = Mongoose.model('Animal', catSchema);

    好了,宣传员开心的出去了,开始到处撩猫去了,把所有它认为是猫的生物都记录下来:

    const kitty1 = new CatGuide({ name: 'mimi', type: 'American Shorthair', meow: true });const kitty2 = new CatGuide({ name: 'kaddy', type: 'American Shorthair', meow: true });const doggy = new CatGuide({ name: 'doggy', type: 'Dog!!', meow: false });
    ……

    (但是这个不靠谱的,拿着手册也能找错,找了一只穿猫咪衣服的狗!!?)


    因此,我们只把那两只猫保存到数据库里。

    kitty1.save().then(() => console.log('meow'));
    kitty2.save().then(() => console.log('meow'));

    知道了他们之间的关系,剩下的就好办了。就是编程了,编程能解决的事情都是简单的事情。

    怎么用(How)?

    快速开始

    Mongoose的使用流程很简单:

    连接 ——> 定义模式 ——> 生成Model ——> 生成Model实例

    其中,连接,Model,实例是实际跟数据库打交道的,他们之间的关系如下:

    通过一个很简单的例子,我们就可以上手操作MongoDB,然后配合官方API,熟练的人可以直接跳过了,但是如果你想了解更多细节,可以继续看下去,看看每个操作具体还可以怎么配置。

    // step 1 引入Mongooseconst Mongoose = require('Mongoose');// setp2 建立连接Mongoose.connect('MongoDB://localhost/test');// step3 建立模式const catSchema = new Mongoose.Schema({ name: String, type: String });// step4 生成模型const Cat = Mongoose.model('Cat', catSchema);// step5.1 模型直接操作documentCat.create({ name: 'mimi', type: 'American Shorthair' }).then(...)// or
                                                                  // step5.2 生成实例,操作documentconst kitty = new Cat({ name: 'mimi', type: 'American Shorthair' });
    kitty.save().then(() => console.log('meow'));

    连接

    我们都知道,使用数据库的第一步是需要连上数据库,Mongoose提供了一个很简单的方式连接:connect。

    Mongoose.connect(url, [options]);

    注意,现在使用url需要加上 { useNewUrlParser: true }选项,否则可能会报错。 Mongoose.connect(url, { useNewUrlParser: true });

    其中url参数的完整形式,跟MongoDB连接字符串的一致:MongoDB://username:password@host:port/database?options...

    默认情况下,用户名和密码可以省略:

    Mongoose.connect('MongoDB://localhost/db1');

    如果还需要传递用户名、密码,则可以使用如下方式

    Mongoose.connect('MongoDB://username:password@host:port/database?options...');

    connect()方法还接受一个选项对象options,该对象将传递给底层驱动程序。这里所包含的所有选项优先于连接字符串中传递的选项:

    • bufferCommands : 这个选项会关闭Mongoose的缓存机制 ,再深一点章节会说。

    • user/pass :用户名和密码

    • autoIndex :通常MongoDB会自动生成索引,但是有时候如果你不需要,可以把这个选项设置为false

    • dbName :制定数据库名,一般是没办法在连接字符串中指定的情形( MongoDB+srv 语法连接到 MongoDB Atlas

    还有一些选项是用来配置Mongoose的,例如,我之前说的useNewUrlParser  ,还有 pooleSize, bufferMaxEntries,connectTimeoutMS, socketTimeoutMS…… 这些都是比较高级的配置,一般人用不到。如果真的需要用到,可以翻官方文档

    模式

    我们知道,模式其的是规范的作用,因此定义模式主要是定义我们数据的表现形式以及做一些格式约定,方便自动校验。例如,我创建了一个学生的模式,它规定了name和age字段的类型:

    const studentSchema = new Mongoose.Schema({
      name: String,
      age: Number,
    });

    基本类型

    模式有以下10种基本类型,如果只使用基本类型,就输入以下字段就行

    • String | Number | Date | Buffer | Boolean | Schema.Types.Mixed | Schema.Types.ObjectId | Array | Schema.Types.Decimal128 | Map(新加入的ES6的Map)

    注意:1. 如果是Array,可以使用[]代替,你可以可以规定Array内部数据类型,例如数字数组: [Number]。2. 很多人困惑为什么没有Object类型,如果你想定义对象,使用Schema.Types.Mixed就行了。Mixed类型支持多种类型混合,比如数组套对象,对象套数组……

    额外约束

    但是,只规定了基本类型还不够,我想做一些额外的约束,比如姓名长度,年龄大小。因此每种类型还有很多配置项,如果要使用额外的约束,需要使用Object形式,同时,类型通过type引入:

    const studentSchema = new Mongoose.Schema({
      name: { type: String, minlength: 2 },
      age: { type: Number, min: 12, max: 16},
    });

    这里只介绍一些比较常用的,更多使用说明参考官方手册

    所有类型可用:

    • required: boolean or function, 很好理解,如果为真就表示该字段必须要存在

    • default: Any or function, 设置默认值

    • validate: function, 增加一个校验函数

    字符串 String:

    • lowercase/upppercase: boolean, 是否做大小写处理

    • trim: boolean, 是否去除首尾空格

    • match: RegExp, 只有满足特定正则的值才能被接受

    • enum: Array, 只有数组内的值才被接受

    • minlength/ maxLength: Number, 最小、大长度

    数字 Number / 日期 Date

    • min / max: Number / Date (根据类型),必须满足的最大值/最小值

    注意: Date使用的是内置的Date,但是你使用setMonth等内置的方法,不会被Mongoose不会识别到,因此使用doc.save()不会有效果。你必须设置doc.markModified(pathToYourDate)。

    const Assignment = Mongoose.model('Assignment', { name: String, dueDate: Date });const a = new Assignment({ name: 'test', dueDate: Date.now() });
    a.save().then(callback);
    Assignment.findOne({ name: 'test' }, 'name dueDate', (err, doc) => {
    doc.dueDate.setMonth(3);
    doc.save(callback); // THIS DOES NOT SAVE YOUR CHANGEdoc.markModified('dueDate');
    doc.save(callback); // works});

    模型

    有了模式之后,就可以生成模型了,生成模型很简单,就使用model函数就行了。

    // step3 建立模式const catSchema = new Mongoose.Schema({ name: String, type: String });// step4 生成模型const Cat = Mongoose.model('Cat', catSchema);

    注意,虽然所有的例子都是Mongoose.model,但是实际上model是Connect原型的方法:Connection.prototype.model(),即一个连接实例才有model方法。(具体的可以看后面的 再深一点章节)

    其中,model第一个参数就是collection名称(Mongoose会自动 复数化,想修改名称也请看后面章节),同时model会把Schema里的内容复制一份

    官方原文是说复制一份

    The .model() function makes a copy of schema. Make sure that you've added everything you want to schema before calling .model()!

    但是我实际测试,感觉还是引用,在调用model方法之后,通过add(), remove()方法修改Schema依然会影响之前创建的model。

    const catSchema = new Mongoose.Schema({ name: String, type: String });const Cat = Mongoose.model('Cat', catSchema);
    catSchema.remove('type');   // 在创建model后修改Schema会影响之前的modelconst kitty = new Cat({ name: 'mimi2', type: 'American Shorthair' });
    kitty.save().then(() => console.log('meow'));  // type字段不会存储到数据库

    实例

    根据之前的图可以知道,model的实例就是文档,有一些比较方便的操作方法,更多方法参考文档

    get(path,opt) : 获取文档的某个字段,这个函数比较好的是做数据处理,第2个参数可以将该字段转换成其他类型:«Schema|String|Number|Buffer|*»

      doc.get('age') // 47
    
      // dynamic casting to a string
      doc.get('age', String) // "47"

    save():顾名思义保存文档到数据库

    var Tank = Mongoose.model('Tank', yourSchema);var small = new Tank({ size: 'small' });
    small.save(function (err) {  if (err) return handleError(err);  // saved!});

    set(path, value, opt) : 设置文档的某个字段,第三个参数依然可以修改字段类型,同时,可以设置{ strict: false }添加任意字段(回顾 为什么章节)

    // 简单用法doc.set(path, value)// 对象形式用法,修改多个字段doc.set({
        path  : value
      , path2 : {
           path  : value
        }
    })// 把value通过Number进行转换doc.set(path, value, Number)// 填加任意字段doc.set(path, value, { strict: false });

    CRUD操作

    由于对文档的操作通常是在"集合"这个维度,因此大部分的操作都是在model上完成的,除了一些单文档的操作可以在文档这个维度,通过实例完成

    新增文档[s]

    文档保存有三种方法,除了之前介绍的save方法,还可以使用模型的下面两种方法。

    • model.create(object) : 其实就是new model.save()的简写

    • model.insertMany([object]) : 可以批量增加,从内存写到数据库比较实用

    区别就像实例的save是猫主人自己跑过来报名。create,insertMany就是我们工作人员替用户直接录入。

    const catSchema = new Mongoose.Schema({ name: String, type: String });const Cat = Mongoose.model('Cat', catSchema);
    Cat.create({ name: 'mimi', type: 'American Shorthair' }).then(...)  // 插入单条文档Cat.insertMany([{ name: 'mimi', type: 'American Shorthair' }...]).then(...)  // 插入多条文档

    查找文档

    Mongoose的查删改的使用方法基本大同小异,这里重点介绍一下查询,剩下的更新和删除主要介绍方法,不会具体介绍怎么使用

    跟查询有关的方法有…一堆(ByID那一批我排除了,实际用途不大,很多时候我们都不知道文档的_id):

    大部分都大同小意,然后findOne跟find的差别就是,一个是查询所有满足条件的文档,一个是只返回第一个,因此我这里只讲一个find的用法:

    Modle.find.(conditions, [projection], [options], [callback] )
    • conditions «Object» : 查询条件(完全跟MongoDB手册一致)

    • [projection] «Object|String»  : 选取字段,和下面的select方法完全一致(我推荐使用链式调用)

    • [options] «Object» : 查询配置(在再深一点章节再介绍)

    • [callback] «Function»  : 推荐使用Promise写发,不写callback

    一般来说,查找MongoDB中的某个文档,是需要一个查询条件(condition)的,这个查询条件可以简单可以复杂,由于完全跟MongoDB手册一致,本文毕竟不是《MongoDB入门》这里就不做过多叙述,如果后续有时间会把整理的MongoDB手册发上来, 下面的例子也会说明一些。

    这里重点说一下Mongoose自身提供的一些比较使用的方法,这里举官方一个例子来说明一下:

    通常一个标准的MongoDB的查询大概是这样(其中Person是一个model),Mongoose完全支持这种查询方式:

    Person.
      find({    // 这里写查询条件
        occupation: /host/,   // 查询条件可以用正则
        'name.last': 'Ghost',   //  限定name.last 
        age: { $gt: 17, $lt: 66 },   // 限定年龄范围
        likes: { $in: ['vaporizing', 'talking'] }   // 限定喜好必须要在这个数组中
      }).
      limit(10).    // 限定取出的文档条数
      sort({ occupation: -1 }).  // 针对某个字段排序,1表示正序,-1表示逆序
      select({ name: 1, occupation: 1 }).  // 选择取出的字段,1表示取出,0表示不取出,如果不需要id,需要显式写{_id : 0}
      exec(callback);  // 这里使用then也行,完全支持Promise,但是查询不是Promise,这个要注意,后面会说

    但是Mongoose把 常用的方法都提取出来了,例如:where,gt,lt,in……(完整API文档),因此更推荐使用下面这种方式:

    // Using query builderPerson.
      find({ occupation: /host/ }).
      where('name.last').equals('Ghost').
      where('age').gt(17).lt(66).
      where('likes').in(['vaporizing', 'talking']).
      limit(10).
      sort('-occupation').
      select('name occupation').
      exec(callback);

    要强调一下,查询支持promise一些语法,但是它不是promise。这个坑必须要记得!! 千万不要将callback形式和promise混用!

    例如,下面的query可以执行三次!并不会因为一次then或者callback结束。 说明then只是一个执行方法,查询并不是真正的Promise。

    const q = MyModel.updateMany({}, { isDeleted: true }, function() {  console.log('Update 1');
    });
    q.then(() => console.log('Update 2'));
    q.then(() => console.log('Update 3'));

    如果要统计查询的文档数据,可以使用countDocuments(),用法和MongoDB的count()一致

    Person.find(...)
    .count()
    .then() // 这里获得的是一个数字

    注意: Mongoose的count()已经被废弃,需要统计个数得用countDocuments

    更新文档

    更新文档的方法有下面三个,condition的使用方法和find的方法一完全一致(包括where那些查询方法都可以使用),只是要注意, update只是更新字段(符合Schema),但是replace是完全的用新doc来替换。

    删除文档

    删除文档主要是两个方法,使用方法完全和查询一致。

    再深一点

    细品连接

    缓存机制

    根据上面的例子,你会发现一个奇怪的现象, 我们并没有判断连接是否成功就进行了操作?这是因为Mongoose内部缓存了函数调用。

    Mongoose lets you start using your models immediately, without waiting for Mongoose to establish a connection to MongoDB

    这很cool,很方便,我们不用去关心连接成功再去增加文档,否则你必须要这样:

    Mongoose
        .connect('MongoDB://localhost/test')
        .then(() => {        const catSchema = new Mongoose.Schema({ name: String, type: String });        const Cat = Mongoose.model('Cat', catSchema);        const kitty = new Cat({ name: 'mimi', type: 'American Shorthair' });
            kitty.save().then(() => console.log('meow'));
        })
        .catch(err => console.log(err));

    但是这样会带来一个问题就是你如果不建立连接,进行操作也不会收到任何提示。

    var MyModel = Mongoose.model('Test', new Schema({ name: String }));// Will just hang until Mongoose successfully connectsMyModel.findOne(function(error, result) { /* ... */ });
    
    setTimeout(function() {
      Mongoose.connect('MongoDB://localhost:27017/myapp');
    }, 60000);

    所以一定要养成良好的喜欢,在做查询,删除等操作的时候,判断一下是否已经连接。判断连接可以使用下面提的 connection对象的连接状态码

    connection对象

    说实话,刚开始使用Mongoose的时候我总是充满疑惑,一开始我认为Mongoose应该是一个类,它代表一次连接,通过new的方式可以创建一个数据库的连接。但是Mongoose不是,它直接通过connect方法就创建了一个连接,就能直接操作数据库上的集合,那说明这个Mongoose只是一个实例(的确也是)。那如果Mongoose只是一个实例,它就等于那一个连接吗?那为什么Mongoose上面没有操作数据库的方法?而且我要如何创建多个连接呢?一个个问题在脑海中回荡,脑子里早已一团浆糊,于是,还是回到了官方文档找答案。

    其实,每次执行Mongoose.connect,实际就是创建了一个Connection对象(这个对象可以通过Mongoose.connection获得)这个对象才是真正对应的一个数据库的连接。因此,所有的数据库操作其实都是在connect对象中,例如:createCollection()dropCollection(), dropDatabase() ……

    这个connection对象同时可以监听几个常用事件:connected,disconnected,error。

    const connection = Mongoose.connection;
    connection.on('connected', () => {  console.log('Mongoose connection open');
      connection.createCollection('test').then(() => console.log('add collection')),
    });

    连接状态码

    为了方便查看对象的状态,connection也提供了一个readyState属性,可以方便判断当前连接状态,通过Mongoose.STATES能获取readyState的所有取值:

    { '0': 'disconnected',
      '1': 'connected',
      '2': 'connecting',
      '3': 'disconnecting',
      '99': 'uninitialized',
      disconnected: 0,
      connected: 1,
      connecting: 2,
      disconnecting: 3,
      uninitialized: 99 }

    所以,理论上你可以这样判断连接是否建立

    if (Mongoose.STATES[connection.readyState] === 'connected') ....

    多个连接

    同时,如果你想创建多个连接,你可以使用Mongoose.createConnection()的方式创建一个新的连接,参数什么的和connect一致,剩下的操作一致。例如以下这个官方例子:

    var Mongoose = require('Mongoose');var db = Mongoose.createConnection(..);
    db.model('Venue', new Schema(..));var Ticket = db.model('Ticket', new Schema(..));var Venue = db.model('Venue');

    细品模式

    虚属性

    虚属性(Virtuals)是Mongoose提供的一个很有用的功能,它可以方便我们存储一些不需要存储在数据库中的数据。例如,我存储一个用户的姓名,只需要存名和姓:

      const personSchema = new Schema({
        name: {
          first: String,
          last: String
        }
      });  const Person = Mongoose.model('Person', personSchema);  const voidsky = new Person({
        name: { first: 'void', last: 'sky' }
      });

    但是我在读出的时候,需要打印用户的全名,当然我们可以这样做:

    console.log(voidsky.name.first + ' ' + voidsky.name.last); // void sky

    但是虚属性提供了一个更方便的方式:

    personSchema.virtual('fullName').get(function () {  return this.name.first + ' ' + this.name.last;
    });console.log(voidsky.fullName);

    如果你用过一些前端框架,你可以把它当作计算属性。但是这个属性不会被持久化到MongoDB中,虚函数有get和set的方法,熟悉setter和getter的就应该知道,一样一样的。

    虚属性还提供了一个很有用的Aliases字段,它可以把Schema的某个属性完全跟一个虚属性进行连接(set和get的绑定),你操作这两个属性的效果完全一致,这个好处是你可以在存储的时候节约空间,但是获取数据的时候,使用语义化的描述 。只是Aliases定义的属性不会被持久化到数据库。这里看官方案例:

    const personSchema = new Schema({
      n: {
        type: String,
        alias: 'name'   // 注意这里,等于创建了一个name的虚属性,绑定到n
      }
    });const Person = Mongoose.model(personSchema);const person = new Person({ name: 'Val' });console.log(person); // { n: 'Val' }console.log(person.toObject({ virtuals: true })); // { n: 'Val', name: 'Val' }console.log(person.name); // "Val"person.name = 'Not Val';console.log(person); // { n: 'Not Val' }

    子模式

    schema是可以嵌套的,如果我们需要定义一个复杂的结构,可以使用嵌套的方式(通常子模式会配置成{ _id: false },具体可以参考下面的模式配置)

    const childSchema = new Schema({
      n: {
        type: String,
        alias: 'name'
      }
    }, { _id: false });const parentSchema = new Schema({  // If in a child schema, alias doesn't need to include the full nested path
      c: childSchema,
      name: {
        f: {
          type: String,      // Alias needs to include the full nested path if declared inline
          alias: 'name.first'
        }
      }
    });

    模式配置

    schema是有很多配置的,你可以在定义Schema的时候配置,或者使用set方法。

    new Schema({..}, options);// orconst schema = new Schema({..});
    schema.set(option, value);

    相关配置可以参考文档,这里举例几个可能会用到的:

    • colleciton 这个配置可以修改collection的名字(默认是使用model的复数化名称,见下面)

    const dataSchema = new Schema({..}, { collection: 'data' });const dataModel = Mongoose.model('othername', dataSchema);

    模式方法

    (未完待续)

    额外推荐:可视化客户端

    当然,你可以通过命令行查看结果,但是更多人还是习惯使用图形化的界面,这里推荐一个MongoDB的可视化客户端:robot 3T,这就是原来大名鼎鼎的RoboMongo。全平台都支持。要是之前我还真不想推荐它,因为在mac上显示实在难受,不支持retina屏幕。

    但是现在,已经好了很多


    免费体验云安全(易盾)内容安全、验证码等服务

    更多网易技术、产品、运营经验分享请点击




    相关文章:
    【推荐】 数据迁移的应用场景与解决方案Hamal

  • 相关阅读:
    linux驱动开发学习一:创建一个字符设备
    如何高效的对有序数组去重
    找到缺失的第一个正整数
    .NET不可变集合已经正式发布
    中国人唯一不认可的成功——就是家庭的和睦,人生的平淡【转】
    自己动手搭建 MongoDB 环境,并建立一个 .NET HelloWorld 程序测试
    ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
    自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
    ServiceStack 介绍
    一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL
  • 原文地址:https://www.cnblogs.com/163yun/p/9887425.html
Copyright © 2011-2022 走看看