MEAN Stack - MongoDB

该文章为网络课 Introduction to MongoDB using the MEAN Stack学习笔记。

1. Mongoose

1.1 简介

是一个在node.js上使用的API。功能上作为一种ODM(Object Document Mapper),可以让程序猿以一种面向对象的方式进行数据的创建和存取,将对象转换为实际存储的BSON document则由mongoose来内部转换。

Mongoose

1.2 Mongoose的四种数据类型

  • connection

一些socket对象,这些socket将mongoose连接到了mongoDB的某个database。

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!
});

连接上了db之后,所有代码在'open'事件的callback里面写。

  • schema

一些对document所包含内容的规定,比如,包含的field,每个field的一些性质等等

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var blogSchema = new Schema({ 
  title: String, 
  author: String, 
  body: String, 
  comments: [{ body: String, date: Date }], 
  date: { type: Date, default: Date.now }, 
  hidden: Boolean, 
  meta: { votes: Number, favs: Number }});
  • 每一个key定义了一个对象的property,即对应了document中的一个property
  • 每一个value对应了property的类型(SchemaType)
  • SchemaType可以是数组,如comment
  • SchemaType可以是nested Key/Value 结构,如meta
  • model

可以把model看作是面向对象中的Class,它利用了schema进行定义,并且在之后用来构造document。

var Blog = mongoose.model('Blog', blogSchema, 'blogs');

此处定义了一个名为'Blog'并且利用了blogSchema的model, 它所对应数据库中的collection名字叫‘blogs’。注意,当第三个参数省略时,Mongoose automatically looks for the plural version of your model name.

  • document
    就是一个model的一个实例化对象(instance),对应着数据库中的一个document。
var blog = new Blog({
    title: "kevin's blog"
    ...
});
Mongoose四种数据类型

2. Schema Design

2.1 Rule 1: Store What You Query For

MongoDB的改写和读取单个document的数据很快,但是它不支持join操作,因此,它不能够用一条语句来对不同document对象的数据进行query。因此,在设计schema之初,设计者最好能够以后要在一条query中取得的东西存在一个document之中,即"The way you store data should reflect how you use it in your application"

2.2 Rule 2: Least Cardinality

由于MongoDB的document中可以存array,因此看起来似乎解决了oneToMany的关系,但是,由于MongoDB中对单个文件的长度有限制,因此,这个性质不建议用当所要存储在array中的对象的数量没有上限时的应用。这时候应该将存储denormalize分散开来。

3. 更多

3.1 Index

将document中的某个field进行index来提升利用这个field查询数据时的效率。原理是:

  • 默认情况下,当利用某个field对某个document进行查询时,MongoDB会扫描(scan)整个数据库,直到找到包含对应的field的那个文件。
  • 当对某个field创建index时,MongoDB会区创建一个数据结构,这个结构类似于HashMap,key是filed,value是对应的document。因此可以很快地利用field找到对应的document。

当需要index的field是个array时,这时候叫做multi-key index

index有两种方式:

  1. 在filed定义的时候( field level)
  2. 在schema定义完之后(schema level), 组合索引(compound indexes)只能是这种定义方式。
var animalSchema = new Schema({ 
    name: String, 
    type: String, 
    tags: { type: [String], index: true } // field level
});

animalSchema.index({ name: 1, type: -1 }); // schema level
  • compund index: 先对name排序,在此基础上再对type排序
  • 1表示index order是ascending,-1表示descending

3.2. Virtuals

mongoose可以利用已经在schema中定义的属性(property)来定义一种虚拟属性(property)。之所以是虚拟的,是在于这种属性并不是真实存在document中的,即并不是真实存于数据库中的。这种做是为了方便能够对query结果的定制化

举个例子:
schema, model, document定义如下:

// define a schemavar 
personSchema = new Schema({ name: { first: String, last: String }});
// compile our modelvar 
Person = mongoose.model('Person', personSchema);
// create a documentvar 
bad = new Person({ name: { first: 'Walter', last: 'White' }});

假设我们需要得到文件bad的full name,一种做法是:

console.log(bad.name.first + ' ' + bad.name.last); // Walter White

利用virtuals,可以对schema定义一个virtual property getter

personSchema.virtual('name.full').get(function () { 
  return this.name.first + ' ' + this.name.last;
});

那么在query全名的时候,就可以很方便地直接用bad.name.full:

console.log('%s is insane', bad.name.full); // Walter White is insane

值得注意的是,如果因为某些需求,需要将document利用JSON.stringify()转成JSON string或者利用.toObject()转成一个plain Object时,default情况下并不能把virtual property转过来,如果想要将其保留过来的话,需要对schema做额外的设置:

schema.set('toObject', {virtuals: true});
schema.set('toJSON', {virtuals: true});

3.3 Custom Setter/Setter

Customer Setter

  • 例子1:在将数据存到mongoDB之前,需要做一些处理,比如说对用户注册的邮箱进行小写化。
function toLower (v) {
  return v.toLowerCase();
}

var UserSchema = new Schema({
  email: { 
    type: String, 
    set: toLower //转小写setter
  } 
});

var User = mongoose.model('User', UserSchema);
var user = new User({email: 'AVENUE@Q.COM'});

console.log(user.email); // 'avenue@q.com'
  • 例子2:
    对document中的某个property进行改动时,有时候需要连带对其他peroperty做出相应的改变。比如说商品始终有一个美元价priceUSD,当时在对document赋值的时候价格上可以使不同的币种,因此这样对价格的设置需要连带改动priceUSD。
// 此处只列出了schema定义的一部分
  ...
  price: {
    amount: { 
      type: Number,
      required: true,
      set: function(v) { //转USD setter
        this.internal.approximatePriceUSD =
          v / (fx()[this.price.currency] || 1);
        return v;
      }
    },
    // Only 3 supported currencies for now
    currency: {
      type: String,
      enum: ['USD', 'EUR', 'GBP'],
      required: true,
      set: function(v) { //转USD setter
        this.internal.approximatePriceUSD =
          this.price.amount / (fx()[v] || 1);
        return v;
      }
    }
  },
  ...

Customer Getter

当从mongoDB取出数据之后,需要做一些处理,使得document对象的value是处理后的值。

  • 例子:
    数据库中存的信用卡卡号是完整的,但是当对进行query时,只想让其显示后4位。
function obfuscate (cc) {
  return '****-****-****-' + cc.slice(cc.length-4, cc.length);
}

var AccountSchema = new Schema({
  creditCardNumber: {  
    type: String, 
    get: obfuscate // 模糊化getter
  }
});

var Account = mongoose.model('Account', AccountSchema);

Account.findById( someId, function (err, found) {
  console.log(found.creditCardNumber); // '****-****-****-1234'
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • Mongodb 配置选项 通常在mongod.conf中 配置文件 设置了配置文件后启动时以自定义的配置文件启动:...
    AkaTBS阅读 1,071评论 0 6
  • 参考深入浅出mongoose 连接mongoose mongoose连接数据库有两种方式第一种: 第二种: mon...
    bacbcc94613b阅读 12,309评论 1 27
  • 参考资料https://www.npmjs.com/package/mongodbhttps://docs.mon...
    程序员有话说阅读 750评论 0 4
  • FlexBox布局 直接阅读大神文章:阮一峰写的FlexBox布局。在react-native中原理是一样的,只不...
    甘言川阅读 635评论 0 2