feng xiaohan

Mongoose

简介

Mongoose 是一个ODM(对象文档映射库—Object-Document Mapping Library)

Mongoose 为模型提供了一种直接的,基于 scheme 结构去定义你的数据模型,然后通过实例化模型来生成一条数据文档;而在数据模型上可以使用各种内置的方法。

例如:

db.collection("users").insertOne({
  name: "Jon",
  age: 28,
  password: "hsdjkffds",
});

——>

const user = User.create({ name: "Jon", age: 28, password: "hsdjkffds" });

安装

npm install --save mongoose

基础使用

连接数据库

app.js

const mongoose = require('mongoose'); // 引入mongoose
...
mongoose
  .connect(
    'mongodb+srv://<user>:<password>@cluster0.mnle1m2.mongodb.net/shop?retryWrites=true&w=majority'
  ) // 连接MongoDB数据库
  .then(result => {
    app.listen(3000);
  })
  .catch(err => {
    console.log(err);
  });

创建和定义模型的 Schema

以键值对的方式来定义模型:

models/product.js

const mongoose = require("mongoose");
const Schema = mongoose.Schema;

const productSchema = new Schema({
  // @1
  title: {
    type: String, // 类型
    required: true, // 是否必须
  },
  price: {
    type: Number,
    required: true,
  },
  description: {
    type: String,
    required: true,
  },
  imageUrl: {
    type: String,
    required: true,
  },
});

module.exports = mongoose.model("Product", productSchema); // 将导出的模块作为mongoose的model,方便日后连接 @2

@1:虽然 MongoDB 是没有特定的 schema 限制的,但通常我们在使用时知道某种数据存在某种结构(比如知道 name 数据用 String 类型);因此 mongoose 需要我们提供这样的结构,让我们只关注于数据本身,即使这样做会放弃一些数据的灵活性。

@2:第一个参数为自定义的集合名字(Mongoose 会将这个名字的开头转为小写,并在后面加上 s 作为集合的名字),第二个参数为需要导出的模块。

自定义 Schema 的方法

我们可以向定义的 Schema 中添加方法,让每个通过这个 Schema 模型创建出来的实例(数据/文档)都可以使用。

const productSchema = new Schema({
  ...
});
productSchema.methods.addToCart = function(product) {
  ...
} // 向productSchema中添加addToCart方法

向集合中新增一条数据(文档)

集合名字在导出时通过mongoose.model已经定义好了,当我们通过创建模型的实例时会添加到对应的集合中,然后通过save()来保存一条数据:

controllers/admin.js

const Product = require("../models/product");

exports.postAddProduct = (req, res, next) => {
  const title = req.body.title;
  const imageUrl = req.body.imageUrl;
  const price = req.body.price;
  const description = req.body.description;
  const product = new Product({
    // 创建模型实例并写入数据
    title: title,
    price: price,
    description: description,
    imageUrl: imageUrl,
  });
  product
    .save() // 保存一条数据
    .then((result) => {
      console.log("Created Product");
      res.redirect("/admin/products");
    })
    .catch((err) => {
      console.log(err);
    });
};

查询数据

查询所有数据

使用find()查询所有的数据:

const Product = require("../models/product");

exports.getProducts = (req, res, next) => {
  Product.find() // 查询所有数据
    .then((products) => {
      res.render("shop/product-list", {
        prods: products,
        pageTitle: "All Products",
        path: "/products",
      });
    })
    .catch((err) => console.log(err));
};

查询条件数据

  • **findById()**——根据 id 查询数据

    exports.getProduct = (req, res, next) => {
      const prodId = req.params.productId;
      Product.findById(prodId) // 根据id(_id)查询数据
        .then((product) => {
          res.render("shop/product-detail", {
            product: product,
            pageTitle: product.title,
            path: "/products",
          });
        })
        .catch((err) => console.log(err));
    };
    

更新数据

更新数据只需要找到对应数据修改后,再使用save()

exports.postEditProduct = (req, res, next) => {
  const prodId = req.body.productId;
  const updatedTitle = req.body.title;
  const updatedPrice = req.body.price;
  const updatedImageUrl = req.body.imageUrl;
  const updatedDesc = req.body.description;

  Product.findById(prodId) // 根据id找到对应数据
    .then((product) => {
      // 对数据进行修改
      product.title = updatedTitle;
      product.price = updatedPrice;
      product.description = updatedDesc;
      product.imageUrl = updatedImageUrl;
      return product.save(); // 保存新修改的数据
    })
    .then((result) => {
      console.log("UPDATED PRODUCT!");
      res.redirect("/admin/products");
    })
    .catch((err) => console.log(err));
};

删除数据

根据 id 删除数据

可以使用 Mongoose 内置的findByIdAndRemove来删除对应数据。

exports.postDeleteProduct = (req, res, next) => {
  const prodId = req.body.productId;
  Product.findByIdAndRemove(prodId) // 删除数据
    .then(() => {
      console.log("DESTROYED PRODUCT");
      res.redirect("/admin/products");
    })
    .catch((err) => console.log(err));
};

嵌入式文档

在 MongoDB 中,集合和集合之间一般是没有关联的,但可以使用嵌入式文档,将复制的关联 id 产生联系。

创建关系

如果想让一个集合和另一个集合有关系,可以使用ref来嵌入关系。

models/product.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const productSchema = new Schema({
  ...
  userId: {
    type: Schema.Types.ObjectId,
    ref: 'User', // 设置关联
    required: true
  }
});

module.exports = mongoose.model('Product', productSchema);

models/admin.js

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const userSchema = new Schema({
  ...
  cart: {
    items: [
      {
        productId: { type: Schema.Types.ObjectId, ref: 'Product', required: true }, // 设置关联
        quantity: { type: Number, required: true }
      }
    ]
  }
});

module.exports = mongoose.model('User', userSchema);

附录

const Product = require('..');
const product = new Product({...});

save()

向集合中保存/新增一条数据。

product.save();

find()

查询所有数据。返回一个 Promise。

Product.find();

findOne()

查询所有数据但只返回一条数据(文档)。返回一个 Promise。

Product.findOne();

findById()

根据 id(_id)查询数据。返回一个 Promise。

Product.findById(prodId);

findByIdAndRemove()

根据 id(_id)删除数据。

Product.findByIdAndRemove(prodId);

deleteOne()

根据指定信息删除一条数据。

Product.deleteOne({ _id: prodId, userId: req.user._id });

populate()

填充字段,将文档中的指定路径自动替换为来自其他集合的文档的过程。返回一个 Promise。

Product.find().populate();

select()

筛选字段,将数据中与之匹配的筛选出来。

Product.find().select();

以字符串空格的方式筛选不同字段:

Product.find().select("title price _id");