Mongoose
Mongoose - это ORM (Object Relational Mapping - объектно-реляционное отображение или связывание) для
MongoDB
.Mongoose
предоставляет в распоряжение разработчиков простое основанное на схемах решение для моделирования данных приложения, включающее встроенную проверку типов, валидацию, формирование запросов и хуки, отвечающие за реализацию дополнительной логики обработки запросов.
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/test', {
useNewUrlParser: true,
useUnifiedTopology: true
})
const Cat = mongoose.model('Cat', { name: String })
const kitty = new Cat({ name: 'Cat' })
kitty.save().then(() => console.log('мяу'))
Быстрый старт
Установка
yarn add mongoose
# или
npm i mongoose
Подключение к БД
const mongoose = require('mongoose')
const { MONGODB_URI } = require('./config.js')
mongoose.connect(MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
Обработка ошибки и подключения
const db = mongoose.connection
db.on('error', console.error.bind(console, 'Ошибка подключения: '))
db.once('open', () => {
// Подлючение установлено
})
Создание схемы и модели
const { Schema, model } = mongoose
const catSchema = new Schema({
name: String
})
module.exports = model('Cat', catSchema)
Создание объекта
const Cat = require('./Cat.js')
const kitty = new Cat({ name: 'Cat' })
Добавление метода
catSchema.methods.speak = function () {
const greet = this.name
? `Меня зовут ${this.name}`
: `У меня нет имени`
console.log(greet)
}
const kitty = new Cat({ name: 'Cat' })
kitty.speak() // Меня зовут Cat
Сохранение объекта
kitty.save((err, cat) => {
if (err) console.error(err)
cat.speak()
})
Поиск всех объектов определенной модели
Cat.find((err, cats) => {
if (err) console.error(err)
console.log(cats)
})
Поиск одного объекта (по имени)
Cat.findOne({ name: 'Cat' }, callback)
Для поиска объекта можно использовать регулярное выражение.
Схема
Создание схемы
const { Schema } = required('mongoose')
const blogSchema = new Schema({
title: String,
author: String
body: String
comments: [
{
body: String
createdAt: Date
}
],
createdAt: {
type: Date,
default: Date.now()
},
hidden: Boolean,
meta: {
votes: Number,
favs: Number
}
})
Дополнительные поля можно доба влять с помощью метода Schema.add()
const firstSchema = new Schema()
firstSchema.add({
name: 'string',
color: 'string',
price: 'number'
})
const secondSchema = new Schema()
secondSchema.add(firstSchema).add({ year: Number })
По умолчанию Mongoose
добавляет к схеме свойство _id
(его можно перезаписывать)
const schema = new Schema()
schema.path('_id') // ObjectId { ... }
const Model = model('Test', schema)
const doc = new Model()
doc._id // ObjectId { ... }
Кроме методов экземпляра, схема позволяет определять статические функции
catSchema.statics.findByName = function(name) {
return this.find({ name: new RegExp(name, 'i') })
}
// или
catSchema.static('findByName', function(name) { return this.find({ name: new RegExp(name, 'i') }) })
const Cat = model('Cat', catSchema)
const cats = await Cat.findByName('Kitty')
Также для определения дополнительного функционала можно использовать утилиты для формирования запроса
catSchema.query.byName = function(name) { return this.where({ name: new RegExp(name, 'i') }) }
const Cat = model('Cat', catSchema)
Cat.find().byName('Cat').exec((err, cats) => {
console.log(cats)
})
Cat.findOne().byName('Cat').exec((err, cat) => {
console.log(cat)
})
Mongoose
позволяет определять вторичные индексы в Schema
на уровне пути или на уровне schema
const catSchema = new Schema({
name: String,
type: String,
tags: { type: [String], index: true } // уровень поля
})
catSchema.index({ name: 1, type: -1 }) // уровень схемы
При запуске приложения для каждого индекса вызывается createIndex()
. Индексы полезны для разработки, но в продакшне их лучше не использовать. Отключить индексацию можно с помощью autoIndex: false
mongoose.connect(MONGODB_URL, { autoIndex: false })
// или
catSchema.set('autoIndex', false)
// или
new Schema({...}, { autoIndex: false })
Схема позволяет создавать виртуальные свойства, которые можно получать и устанавливать, но которые не записываются в БД
// Схема
const personSchema = new Schema({
name: {
first: String,
last: String
}
})
// Модель
const Person = model('Person', personSchema)
// Документ
const john = new Person({
name: {
first: 'John',
last: 'Smith'
}
})
// Виртуальное свойство для получения и записи полного имени
personSchema.virtual('fullName')
.get(function() { return `${this.name.first} ${this.name.last}` })
.set(function(str) {
;[this.name.first, this.name.last] = str.split(' ')
})
john.fullName // John Smith
john.fullName = 'Jane Air'
john.fullName // Jane Air
Настройки
Настройки схемы могут передаваться в виде объекта в конструктор или в виде пары ключ/значение в метод set()
new Schema({...}, options)
const schema = new Schema({...})
schema.set(option, value)
autoIndex: bool
- определяет создание индексов (по умолчаниюtrue
)autoCreate: bool
- определяет создание коллекций (по умолчаниюfalse
)bufferCommands: bool
иbufferTimaoutMs: num
- определяет, должны ли команды буферизоваться (по умолчаниюtrue
), и в течение какого времени (по умолчанию отсутствует)capped: num
- позволяет создавать закрытые коллекции ({ capped: 1024 }
, 1024 - размер в байтах)collection: str
- определяет название коллекции (по умолчанию используется название модели)id: bool
- позволяет отключать получение_id
через виртуальный геттерid
_id: bool
- позволяет отключать создание_id
minimize: bool
- определяет удаление пустых объектов (по умолчаниюtrue
). Для определения пустого объекта используется утилита$isEmpty
-doc.$isEmpty(fieldName)
strict: bool
- определяет, должны ли значения, передаваемые в конструктор модели и отсутствующие в схеме, сохраняться в БД (по умолчаниюtrue
, значит, не должны)typeKey: str
- позволяет определять ключ типа (по умолчаниюtype
)validateBeforeSave: bool
- позволяет отключать валидацию объектов перед их сохранением в БД (по умолчаниюtrue
)collation: obj
- определяет порядок разрешения коллизий, например, при совпадении двух объектов ({collation: { locale: 'en_US', strength: 1 }}
- совпадающие ключи/значения на латинице будут игнорироваться)timestamps: bool | obj
- позволяет добавлять к схеме поляcreatedAt
иupdatedAt
с типомDate
. Данным полям можно присваивать другие названия -{ timestamps: { createdAt: 'created_at' } }
. По умолчанию для создания даты используетсяnew Date()
. Это можно изменить -{ timestamps: { currentTime: () => ~~(Date.now() / 1000) } }
Метод loadClass()
позволяет создавать схемы из классов:
- методы класса становятся методами
Mongoose
- статические методы класса становятся статическими методами
Mongoose
- геттеры и сеттеры становятся виртуальными методами
Mongoose
class MyClass {
myMethod() { return 42 }
static myStatic() { return 24 }
get myVirtual() { return 31 }
}
const schema = new Schema()
schema.loadClass(MyClass)
console.log(schema.methods) // { myMethod: ... }
console.log(schema.statics) // { myStatic: ... }
console.log(schema.virtuals) // { myVirtual: ... }