mongoose介绍及应用

mongoose介绍及基本使用

标签(空格分隔): 未分类

两种和数据库交互的方式

  1. 使用原生语言查询 eg:select * from xx ...
  2. 使用ODM/ORM(Object Data Model ("ODM") / Object Relational Model ("ORM")),ODM/ORM
    表示把数据当作js对象,然后把它映射到底层数据库。一些ORM与特定数据库绑定,而其他ORM则提供与数据库无关的后端

使用SQL或数据库支持的查询语言可以获得最佳性能。

ODM通常较慢,因为它们使用翻译代码来映射对象和数据库格式,并不高效(如果ODM支持不同的数据库后端,则尤其如此,并且必须对数据库进行更多支持)

使用ORM的好处是程序员可以继续思考JavaScript对象而不是数据库语义 - 如果您需要使用不同的数据库(在相同或不同的网站上),这一点尤其如此。它们也提供了一个明显的地方进行数据的验证和检查。

出除非你对原生数据库操作非常熟悉,否则就使用ODM

使用mongoosemongodb

mongoosemongodb这种ODM和数据库组合在Node社区中非常受欢迎,部分原因是文档存储和查询系统看起来非常像JSON,因此JavaScript开发人员很熟悉。

设计 LocalLibrary models

下面来设计一个图书馆的数据库

我们知道信息包括图书(标题,主题,作者,类型,ISBN)和其他属性(id,statuses),我们可能要存储更多的信息,不止他的作者,因为可能存在相似或者相同的人名。我们也想根据title,category,genre,author来排序

当我们设计model时,把每个object分开是很有意义的。很明显对象包括books,book instance,author

您可能还想使用模型来表示选择列表选项(例如,像下拉列表中的选项),而不是将选择硬编码到网站本身中 - 当所有选项在前面未知时,建议使用可能会改变。这种类型的模型的明显候选者是书籍类型(例如科幻小说,法国诗歌等)

设计模型时我们需要考虑他们之间的关系

我们为书创建的models包括book(包含基本信息)book instance(书的状态)author(作者),我们也要决定models的类型。

该图还显示了模型之间的关系,包括它们的多重性。多重性是图中的数字,显示可能存在于关系中的每个模型的数量(最大值和最小值)。例如,盒子之间的连接线显示书和类型相关。书模型中的数字表明,一本书必须具有零个或多个类型(尽可能多的喜欢),而类型旁边的行的另一端的数字表明它可以具有零个或多个关联的书。

mongoose 入门

安装看这里,本地启动也看这里here
npm install mongoose --save

连接mongodb

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:'));

你可以使用默认的connection对象即mongoose.connection,一旦连接,open事件就会被触发。

定义创造models

models使用Schema接口,Schema允许你定义数据的类型,默认值和要求等。额外你还能定义一些虚拟方法来使你处理数据更加方便,这些不会存在数据库中

Schema使用mongoose.model()方法,一旦你创建一个model,你可以对他进行CURD

定义 SCHEMA

//Require Mongoose
var mongoose = require('mongoose');

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
    a_string          : String,
    a_date            : Date
});

首先引入 mongoose ,定义schma,接着创造一个新实例

创建一个model

models被从schema中通过mongoose.model()来创建

// 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 );

第一个参数是你要创建的models的名字,数据库将会创建一个someModelcollection,第二个参数是schema

schema类型

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

上面代码也向我们展示了两种定义schema的方式

+ 以`name`和`type`的ket-value方式
+ 以`name`后面接对象的形式,对象包括
    + 默认值
    + 验证(max/min)和验证函数
    + 是否required
    + 是否被设置为大小写,去空格

验证

mongoose提供了 built-incustom validators,synchronous , asynchronous validator
它允许你在所有情况下同时指定可接受的范围或值以及验证失败的错误消息。

一个built-in validator包括

  1. 所有SchemaTypes都需要包括require,这用于指定是否必须提供该字段才能保存文档。
  2. Numbers拥有maxmin
  3. String拥有
    • enum : 可枚举的值
    • match: 字符串规则
    • maxlength和minlength

虚拟属性

虚拟属性是可以获取和设置的文档属性,但不能保留到MongoDB。 getter对于格式化或组合字段很有用,而setter可用于将单个值解组为多个值进行存储。文档中的示例从第一个和最后一个名称字段构造(并解构)全名虚拟属性,这比每次在模板中使用时构建全名更简单和更清洁。

methods和query helper

schema还可以具有实例方法,静态方法和查询助手。实例和静态方法是相似的,但是明显的区别是实例方法与特定记录相关联,并且可以访问当前对象。查询助手允许您扩展mongoose的可链接查询构建器API(例如,允许您除了find(),findOne()和findById()方法之外)添加查询“byName”)。

使用models

一旦你创建了一个schema,你就可以使用它来创建models.model代表的是collection。你可以保存或者检索

修改文档

要创建一个记录你首先要定义一个model的实例然后调用save()方法

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

注意CURD都是异步操作,当动作完成,执行回调函数。API使用 err作为第一个参数。

您也可以在保存模型实例的同时使用create()来定义模型实例。

SomeModel.create({ name: 'also_awesome' }, function (err, awesome_instance) {
  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.
})

如果不指定回调函数,那么API将返回一个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()函数来实现,我们可以使用点运算符·.将查询的所有部分链接在一起,而不是单独添加。下面的代码片段与上面的查询相同,对于年龄有一个额外的条件。

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()方法得到所有匹配结果记录,一些情况我们只想匹配一个,可以尝下下面的方法

findById() ID查询
findOne() 返回单个记录
findByIdAndRemove(), findByIdAndUpdate(), findOneAndRemove(), findOneAndUpdate(): Finds a single document by id or criteria and either update or remove it. These are useful convenience functions for updating and removing records.

连接相关的documents

您可以使用ObjectId模式字段从一个文档/模型实例创建引用,也可以使用ObjectIds数组从一个文档创建引用。该字段存储相关模型的ID。如果您需要相关文档的实际内容,可以使用查询中的populate()方法将该ID替换为实际数据。

下面例子中,作者可以下面有多个故事书,但每个故事书只有一个作者

ref属性告诉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来关联不同的document,注意下面的bob.id

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和作者表关联了起来,我们通过populate来传递author

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

Schema/model文件分离

虽然您可以使用您喜欢的任何文件结构创建模式和模型,但我们强烈建议您在自己的模块(文件)中定义每个模型模式,导出创建模型的方法。如下图所示:

/ File: ./models/somemodel.js

//Require Mongoose
var mongoose = require('mongoose');

//Define a schema
var Schema = mongoose.Schema;

var SomeModelSchema = new Schema({
    a_string          : String,
    a_date            : Date,
});

//Export function to create "SomeModel" model class
module.exports = mongoose.model('SomeModel', SomeModelSchema );

现在你可以引用models在其他文件中

//Create a SomeModel model just by requiring the module
var SomeModel = require('../models/somemodel')

// Use the SomeModel object (model) to find all SomeModel records
SomeModel.find(callback_function);

实战部分

连接数据库

//Set up mongoose connection
var mongoose = require('mongoose');
var mongoDB = 'insert_your_database_url_here';
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

定义schema

建立下面目录结构

/express-locallibrary-tutorial  //the project root
  /models
    author.js
    book.js
    bookinstance.js
    genre.js

author Schema

./models/author.js

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var AuthorSchema = Schema(
  {
    first_name: {type: String, required: true, max: 100},
    family_name: {type: String, required: true, max: 100},
    date_of_birth: {type: Date},
    date_of_death: {type: Date},
  }
);

// Virtual for author's full name
AuthorSchema
.virtual('name')
.get(function () {
  return this.family_name + ', ' + this.first_name;
});

// Virtual for author's URL
AuthorSchema
.virtual('url')
.get(function () {
  return '/catalog/author/' + this._id;
});

//Export model
module.exports = mongoose.model('Author', AuthorSchema);

book model

./models/boook.js

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookSchema = Schema({
  title: {type: String, required: true},
  author: {type: Schema.ObjectId, ref: 'Author', required: true},
  summary: {type: String, required: true},
  isbn: {type: String, required: true},
  genre: [{type: Schema.ObjectId, ref: 'Genre'}]
});

// Virtual for book's URL
BookSchema
.virtual('url')
.get(function () {
  return '/catalog/book/' + this._id;
});

//Export model
module.exports = mongoose.model('Book', BookSchema);

注意两处ref ref要在schema中声明

BookInstance model

./models/bookinstance.js

var mongoose = require('mongoose');

var Schema = mongoose.Schema;

var BookInstanceSchema = Schema({
  book: { type: Schema.ObjectId, ref: 'Book', required: true }, //reference to the associated book
  imprint: {type: String, required: true},
  status: {type: String, required: true, enum: ['Available', 'Maintenance', 'Loaned', 'Reserved'], default: 'Maintenance'},
  due_back: {type: Date, default: Date.now},
});

// Virtual for bookinstance's URL
BookInstanceSchema
.virtual('url')
.get(function () {
  return '/catalog/bookinstance/' + this._id;
});

//Export model
module.exports = mongoose.model('BookInstance', BookInstanceSchema);

enum:允许我们设置字符串的允许值。在这种情况下,我们使用它来指定我们的书籍的可用性状态(使用枚举意味着我们可以防止错误拼写和我们的状态的任意值)

Genre model

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

推荐阅读更多精彩内容