mongoose介绍及基本使用
标签(空格分隔): 未分类
两种和数据库交互的方式
- 使用原生语言查询 eg:
select * from xx ...
- 使用ODM/ORM(
Object Data Model ("ODM") / Object Relational Model ("ORM")
),ODM/ORM
表示把数据当作js对象,然后把它映射到底层数据库。一些ORM与特定数据库绑定,而其他ORM则提供与数据库无关的后端
使用SQL或数据库支持的查询语言可以获得最佳性能。
ODM通常较慢,因为它们使用翻译代码来映射对象和数据库格式,并不高效(如果ODM支持不同的数据库后端,则尤其如此,并且必须对数据库进行更多支持)
使用ORM的好处是程序员可以继续思考JavaScript对象而不是数据库语义 - 如果您需要使用不同的数据库(在相同或不同的网站上),这一点尤其如此。它们也提供了一个明显的地方进行数据的验证和检查。
出除非你对原生数据库操作非常熟悉,否则就使用ODM
使用mongoose
和mongodb
mongoose
和mongodb
这种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
的名字,数据库将会创建一个someModel
的collection
,第二个参数是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-in
和custom validators
,synchronous
, asynchronous validator
它允许你在所有情况下同时指定可接受的范围或值以及验证失败的错误消息。
一个built-in validator
包括
- 所有
SchemaTypes
都需要包括require
,这用于指定是否必须提供该字段才能保存文档。 -
Numbers
拥有max
和min
-
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:允许我们设置字符串的允许值。在这种情况下,我们使用它来指定我们的书籍的可用性状态(使用枚举意味着我们可以防止错误拼写和我们的状态的任意值)