MongoDB 和 Mongoose基本操作

Why MongoDB & mongoose

image.png

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。
与数据库交互一般有两种方法:

  1. SQL
  2. 对象数据模型(Object Data Model,简称 ODM)或对象关系模型(Object Relational Model,简称 ORM)

使用 ODM / ORM 通常可以降低开发和维护成本!除非你非常熟悉本地查询语言,或者项目对性能要求很高,否则强烈推荐使用 ODM。

mongoose是ODM/ORM的一种流行、活跃的解决方案。

MongoDB

基本概念

image

image

集合

Capped collections 就是固定大小的collection。它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 "RRD" 概念类似。
db.createCollection("mycoll", {capped:true, size:100000})
size是byte

MongoDB 数据类型

除了基本类型,还有Array,Object(用于内嵌文档,类似表联合),ObjectId,Binary Data,Code,Regular expression等。
ObjectId
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

image

ongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象。
-w1013

MongoDB连接

mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]

常用操作(不要忘记ODM的实质)

创建集合
show dbs
use foo
db.createCollection('dashen')
db.dashen.insert({..})
show tables
da.dashen.drop()//删除
在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。
插入文档
db.dashen.insert({})
更新文档
update

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})

db.dashen.save({_id:ObjectId("5d147e652104d5e0077b44f6"),skill:"java"})
删除文档

db.inventory.deleteOne( { status: "D" } )
db.inventory.deleteMany({})

查询文档
db.col.find({"likes": {gt:50},or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
MongoDB 查询数据的语法格式如下:
db.collection.find(query, projection)
>db.col.find({"likes": {$gt:50}, $or: [{"by": "菜鸟教程"},{"title": "MongoDB 教程"}]}).pretty()
排序
1 为升序排列,而 -1 是用于降序排列
>db.COLLECTION_NAME.find().sort({KEY:1})
索引

“A database index is a data structure that improves the speed of data retrieval operations on a database table at the cost of additional writes and storage space to maintain the index data structure. Indices are used to quickly locate data without having to search every row in a database table every time a database table is accessed.”

默认情况下,MongoDB都会建立id的索引,所以对id的检索会比较效率

db.col.getIndexes()
db.col.dropIndex("索引名称")
db.col.createIndex({"createDate": 1},{expireAfterSeconds: 180})

聚合
聚合一系列管道操作,用于处理集合中文档数据(诸如统计平均值,求和等),并返回计算后的数据结果。
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
。。。。
分片、复制、备份和恢复等。

mongoose

基本流程操作

const mongoose = require('mongoose')
// 1.创建连接
const mongoUri = 'mongodb://localhost/foo'
mongoose.connect(mongoUri, {
    useNewUrlParser: true
})
mongoose.Promise = global.Promise
const db = mongoose.connection
// open
db.on('error', (err) => console.log('err'))
db.on('open', (err) => {
    console.log('open')
    // collectCb()
})
// 2.定义模式
const { Schema } = mongoose
const CatSchema = new Schema({
    name :{
        type: String,
        required: true
    },
    birthday :{
        type: Date,
        default: Date.now()
    }
})
// 2.1 模式有一些类型,和验证
const 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], // 其他类型也可使用数组
    nested: {
        stuff: {
            // 是否将 String 字段自动转换为小写、大写,或截断两端空格(例如{ type: String, lowercase: true, trim: true })
            type: String,
            lowercase: true,
            trim: true
        }
    }
})
const breakfastSchema = new Schema({
    eggs: {
        type: Number,
        min: [6, '鸡蛋太少'],
        max: 12
    },
    drink: {
        type: String,
        enum: ['咖啡','茶']
    }
})

// 3.定义Model(创建collection的原型)
const CatModel = mongoose.model('cat', CatSchema)
// 4.新建doc
const createFunc = (() => {
    let catTest;
    for (let i = 3; i > 0; i--) {
        catTest = new CatModel({
            name: `kitty,num:${i}`
        })
        catTest.save((err, doc) => {
            if (!err) {
                console.log(`kitty,num:${i} save success!`)
            }
        })
    }
})()
// 5.查询
const queryFunc = (() => {
    CatModel.find().where('name').equals('kitty,num:1').select('birthday').exec((err,doc) => {
        // ...
    })
})()

文档间协同

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

Story
  .findOne({ title: '司马迁是历史学家' })
  .populate('author') // 使用作者 id 填充实际作者信息
  .exec(function (err, story) {
    if (err) {
      return handleError(err);
    }
    console.log('作者是 %s', story.author.name);
    // 控制台将打印 "作者是 司马迁"
  });

虚拟属性

自定义某个属性(field)get和set方法,该属性并不存于数据库中。
虚拟属性是可以获取和设置、但不会保存到 MongoDB 的文档属性。

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const AuthorSchema = new 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},
  }
);

// 虚拟属性'name':表示作者全名
AuthorSchema
  .virtual('name')
  .get(function () {
    return this.family_name + ', ' + this.first_name;
  });

// 虚拟属性'lifespan':作者寿命
AuthorSchema
  .virtual('lifespan')
  .get(function () {
    return (this.date_of_death.getYear() - this.date_of_birth.getYear()).toString();
  });

// 虚拟属性'url':作者 URL
AuthorSchema
  .virtual('url')
  .get(function () {
    return '/catalog/author/' + this._id;
  });

// 导出 Author 模型
module.exports = mongoose.model('Author', AuthorSchema);

一模式(模型)一文件

强烈建议将单一模式定义在单一模块(文件)中,并通过导出方法来创建模型。
mongoose默认使用当前connect的数据库。

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

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

// 虚拟属性'url':藏书 URL
BookSchema
  .virtual('url')
  .get(function () {
    return '/catalog/book/' + this._id;
  });

// 导出 Book 模块
module.exports = mongoose.model('Book', BookSchema);

测试脚本:https://raw.githubusercontent.com/roy-tian/mdn-examples/master/server/express-locallibrary-tutorial/populatedb.js

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