mongodb和mongoose

2,022 阅读8分钟

前言

如果一个网站需要存储大量的信息,那么就需要用到数据库。数据库的CRUD(增删改查)操作是后端服务器的一项重要的操作。在这篇文章中,我们就来聊一聊数据库。数据库就是一个存放数据的仓库,这个仓库按照一定的数据结构来组织存储数据。按照早期的数据库理论,比较流行的数据库模型有三种,分别是层次式数据库、网络式数据库和关系型数据库(前两者已经基本消失)。当今的互联网中,最常见的数据库模型有两种关系型数据库(RDBMS,Relational Database Management System)和非关系型数据库(noSQL)。关系型数据库,比较著名的有Oracle、MySQL;非关系型数据库,即NoSQL,其含义不是"no sql",而是"not only SQL",不仅仅是SQL。SQL的全称是"Structured Query Language(结构化查询语言)",是专门为数据库而建立的造作命令集,是一种功能齐全的数据库语言。类似JavaScript的声明式编程,在使用时只需要考虑"做什么",而不需要考虑"怎么做"。常见的非关系型数据库有Redis,还有我们今天的主角MongoDB。

mongoDB

mongoDB基本概念

MongoDB是面向文档的数据库,其中有三个重要的概念:数据库(database)、集合(collection)、文档(document).

  • 数据库(database):和SQL中的数据库相对应,表示数据库,可以理解为所有的数据都存在这个空间中
  • 集合(collection):和SQL中的tabel相对应,表示数据表/集合,可以理解为存储的某一类数据
  • 文档(document):和SQL中的row相对应,表示数据记录行/文档,可以理解为存储的某一条数据 显然,这三个概念之间存在一种包含关系,数据库中包含很多个集合,集合中包含很多文档。从数学集合的观点来看,数据库是集合的集合,集合是文档的集合。下图是三者关系的直观表示。
    mongoDB三个概念关系.png
    除此之外,MongoDB中还有三个关键概念:字段(field)、索引(index)、主键(primaryy key).
  • 字段(field):如果说document代表的是SQL语言中的row的话,那么字段这个概念代表的就是SQL中的column。也可以理解为键值对(key-value)中的“键”.
  • 索引(index):这个概念看名称就知道了,无需多言
  • 主键(primary key):MongoDB数据库中的数据的唯一标识,默认主键为_id字段 到此,mongoDB中的基本概念就介绍完了。接下来,我们看一条真实存储在mongoDB数据库中的数据:
  • 在数据库中的样子: mongoDB数据.png 可以看到,该条数据,或者可以说这个文档,有七个字段,主键是_id。当数据通过被查询到之后就会以如下的json的格式返回
{
  "_id" : ObjectId("60d571d606d8634c18cf2338"),
  "agreeArticle" : [],
  "username" : "ggg",
  "password" : "202cb962ac59075b964b07152d234b70",
  "type" : "normal",
  "agreeCount" : 0,
  "__v" : 0
}

使用

在javascript中关于mongoDB的使用,主要是通过中间库mongoose来实现的。因此,很多操作都只需要调用mongoose即可,这些操作将在下一部分进行介绍。在这里,主要介绍mongoDB中文档的CRUD(增删改查)操作。在执行文档的CRUD操作之前,应该现有现有数据库和集合这两个要素。而在所有的操作之前,应该先安装和启动mongoDB,并且建立数据库的连接

安装和启动mongoDB

数据库

关于数据库的操作的关键命令如下:

  • 创建数据库:use DATABASE_NAME
  • 显示数据库列表:show dbs
  • 显示当前数据库:db
  • 删除数据库:db.dropDatabase()

注意:删除数据库的操作需要谨慎使用!!!

集合

关于集合操作的几条关键命令如下:

  • 创建集合:db.createCollection("COLLECTION_NAME")
  • 显示当前数据库中的集合:show collections
  • 删除集合:db.COLLECTION_NAME.drop()

文档

  • 增(create):
    在mongoDB中,我们需要将document插入到collection中。每一个document的数据结构和JSON基本一样。但是这种数据结构在mongoDB中不叫作JSON,而是叫做BSON,是Binary JSON的简称。接下来,我们来关注在collection中插入document的语法。
    • db.COLLECTION_NAME.insert(document):若插入的数据主键已经存在,则会抛出org.springframework.dao.DuplicateKeyException异常,提示主键重复,不保存当前数据
    • db.COLLECTION_NAME.save(document):如果_id主键存在则更新数据,如果不存在就插入数据。该方法在新版本中已废弃,可以用db.collection.insertOne()db.collection.replaceOne()来代替。
    • db.COLLECTION_NAME.insertOne(<document>,{writeConcern:<document>}): 3.2版本之后新增的插入方法。其中,<document>表示要写入的文档;writeConcern表示写入策略,默认为 1,即要求写入操作,0 是不要求。
    • db.COLLECTION_NAME.insertMany([<document 1>, <document 2>, ... ],{writeConcern: <document>, orderd: <boolean>}):3.2版本之后新增的插入方法。其中,<document>writeConcern的含义与之前相同,ordered表示是否按顺序写入,默认为true,按顺序写入。

writeConcern字段的含义,相关文档是这样表述的: Write concern describes the level of acknowledgment requested from MongoDB for write operations to a standalone mongod or to replica sets or to sharded clusters. In sharded clusters, mongos instances will pass the write concern on to the shards. write Concern 包含下列字段:

{
    w: <value>,           
    j: <boolean>,
    wtimeout: <number>
}

详情请参考相关文档,相关文档链接如下:docs.mongodb.com/manual/refe…

  • 删(delete)
    删除文档的语法如下:
    db.collection.remove(
        <query>,
        {
            justOne: <boolean>,
            writeConcern: <document>
        }
    )
    
    • <query>:可选参数,表示删除文档的条件
    • justOne:可选参数,如果设为true 或 1 ,则只删除一个文档;如果不设置该参数,或者使用默认值false,则删除所有匹配条件的文档。
    • writeConcern:可选参数,表示抛出异常的级别。
  • 改(update) 更新文档的语法如下:
    db.collection.update(
        <query>,
        <update>,
        {
            upsert: <boolean>,
            multi: <boolean>,
            writeConcern: <document>
        }
    )
    
    • query:update的查询条件
    • update:update的对象和一些更新的操作符
    • upsert:可选参数,如果不存在update记录,是否插入objNew,true为插入,false为不插入,默认值。
    • multi:可选参数,默认为false,只更新找到的第一条记录;如果设置为true,则更新符合条件的全部数据
    • writeConcern:可选参数,表示抛出异常的级别
  • 查(retrieve)
    mongoDB的查询操作,可以参考下面的文章,此处不再赘述: juejin.cn/post/684490…

mongoose

mongoose是一个使用nodeJS来操作mongoDB数据库的开源库。mongoose里面封装了连接数据库、创建collection和document CRUD的操作。

安装mongoose

在自己的项目中运行:npm install mongoose --save

连接数据库

废话不多说,直接贴代码:

const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/test',
            {useNewUrlParser: true, useUnifiedTopology: true})
const db = mongoose.connection;
db.on('open', function() {
  // 数据库连接成功的回调函数
});

Schema 和 Model

在mongoose中,Schema是起点,它与mongoDB中的collection相对应,并且定义了collection中每一个document的数据结构。下面是一个Schema的使用实例:

import mongoose from 'mongoose';
const { Schema } = mongoose;

const blogSchema = new Schema({
    title:  String, // String is shorthand for {type: String}
    author: String,
    body:   String,
    comments: [{ body: String, date: Date }],
    date: { type: Date, default: Date.now },
    hidden: Boolean,
    meta: {
      votes: Number,
      favs:  Number
    }
});

在这段代码中,规定了blogSchema的字段,以及每一个字段的数据类型。在mongoDB中,collection是document的集合,而Schema对应的是mongoDB的collection。那么,另一个与document对应的概念就是model。一个model的实例就是一个document。因此,mongoDB数据库中数据的增删改查在mongoose中都是利用Model来进行的。使用model的第一步是调用mongoose.model方法:

const Blog = mongoose.model('Blog',blogSchema)

容易看到,这个方法需要传入两个参数,一个是集合的名称,另一个是schema对象。在调用了这个方法之后,可以进行数据的增删改查操作了:


  • 向数据库中添加数据的操作,首先需要实例化一个model对象,这个很容易理解,要向数据库添加数据,首先需要有数据才能够添加。然后,调用model.save()方法即可。例子如下:
    new Blog({
        title:  'xxx', // String is shorthand for {type: String}
        author: 'xxx',
        body:   'xxx',
        comments: [],
        date: Date.now,
        hidden: true,
        meta: {
          votes: 0,
          favs:  0
        }
    }).save((err) => {
        // 这是一个回调函数,处理该条数据保存之后的操作
    })
    
    model.save()返回的是一个promise对象。因此,回调函数也可以携程链式调用的形式。

  • 删除操作有两个方法可以调用:deleteOne()deleteMany()。使用这两个方法,需要传入需要删除的数据的条件
    Tank.deleteOne({ size: 'large' }, function (err) {
      if (err) return handleError(err);
      // deleted at most one tank document
    });
    
    还可以采用Model.findOneAndDelete(),使用方法和前面的一样

  • 更新数据的操作可以采用的方法有:
    • Model.findOneAndUpdate()
    • Model.update()
    • Model.updateMany()
    • Model.updateOne() 需要的参数如下:
    • conditions:需要更新的数据的筛选条件
    • update:更新之后大数据
    • options:可选参数,该方法的一些配置选项
    • callback:执行完更新操作之后的回调函数,这里需要注意一个问题:回调函数有两个参数err和data,这里的data表示的是原始数据,而不是更新完之后的数据。

  • 查询数据库中数据的方法有:
    • Model.find()
    • Model.findOne()
    • Model.findMany() 需要的参数仅仅是更新操作除掉第二个参数,其余都相同。

Query

关于query对象的特性,有很多。但是,似乎在简单的应用里都不怎么能够用得到。但是,可以拿它当promise对象来用。当然,用的时候可以这么用,还是要记住他俩是有区别的。

写在后面的话

这篇文章是我第一次使用mongoDB和mongoose做项目之后总结的一些内容,因此原则是够用就好。显然,它们还有很多很多重要的特性。如果需要用到,必须要参考官方的文档了。
官方文档存档: