Подробное руководство по MongoDB, Mongoose
Введение
MongoDB состоит из БД, которые состоят из коллекций. Коллекции, в свою очередь, состоят из документов. Каждый документ состоит из полей.
www.mongodb.com
MongoDB клиенты
Конфиг mongodb для windows
В папке bin
создайте файл и назовите его mongodb.config
.
Указываем в этом файле путь, где будем хранить БД, например, для windows:
dbpath=c:\mongodb\data
Запуск
mongod
— это сервер баз данных MongoDB. Он обрабатывает запросы, управляет форматом данных и выполняет различные операции в фоновом режиме по управлению БД. Командная строка отобразит нам ряд служебной информации, например, что сервер запускается наlocalhost
на порту27017
.mongo
— клиентская консоль для взаимодействия с базами данных.mongodump
— утилита создания бэкапа БД.mongorestore
— позволяет записывать данные из дампа, созданногоmongodump
, в новую или существующую БД.
Стартуем mongod (сервер):
mongod --config c:\mongodb\bin\mongodb.config // не забудьте заранее создать папку data
Подключаемся к запущенному серверу
Команда mongo
позволяет подключиться к запущенному серверу (стартуем mongo
оболочку/shell).
mongo
Работаем с БД
Выводим все БД в mongo:
show dbs
Переходим (и одновременно создаем) к нужной БД
use name_bd
Команды db
возвращает имя БД, внутри которой мы сейчас находимся:
db //test
Коллекции
Показать все коллекции в БД
show collections
Метод find()
Показать весь контент нужной коллекции:
db.collection_name.find()
Пример:
db.band.find()
db.band.find().pretty()
Метод pretty
выводит результат в удобном для чтения виде.
Метод count()
Метод count
выводит количество документов в коллекции:
db.band.count()
Метод remove()
Метод remove
используется, чтобы удалить документ из коллекции (или всю коллекцию).
db.unicorns.remove({name: "Leto"})
Метод insert()
Заносим данные в коллекцию band
(создаем тем самым коллекцию band
, если ее нет):
db.band.insert({name: 'Queen', bid: '3'})
Добавим составы груп в коллекцию band
:
db.band.update({bid: '1'}, {$set: {members: [ {name: "Jimmy Page", id: "1"}, {name: "robert Plant", id: "2"}, {name: "John Bonham", id: "3"}, {name: "John Paul Jones", id: "4"}]}}) db.band.update({bid: '2'}, {$set: {members: [ {name: "Syd Barret", id: "5"}, {name: "Roger Waters", id: "6"}, {name: "Nick Mason", id: "7"}, {name: "Richard Wright", id: "8"}, {name: "David Gilmour", id: "9"}]}})
Мы можем добавлять данные, не декларируя их предварительно: свойство members
. Отсутствует схема: легко добавили массив объектов.
Мы не обязаны создавать коллекции явно. Мы просто можем вставить документ в новую коллекцию. Чтобы это сделать, используйте команду insert
, передав ей вставляемый документ:
db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})
Модификация данных
Оператор $set
Оператор $set
заставляет команду update
модифицировать лишь те ключи, которые ему переданы (см. пример выше).
Оператор $unset
Оператор $unset
удаляет указанный ключ
db.collection.update({id: 2}, {$unset: {myKey: 1}}); db.example.update({}, {$unset: {words:1}}, false, true);
Оператор $inc
Оператор $inc
увеличивает значение поля на указанную величину
db.collection.update({id: 2}, {$inc: {myCounter: 111}}); db.collection.update( {"players.playerName":"Joe"}, { $inc : { "players.$.playerScore" : 1 } }
Оператор $rename
Оператор $rename
позволяет переименовать поля
db.collection.update({id: 2}, {$rename: {"old_name": "new_name"}}); db.band.update({bid: "1"},{$rename:{"members":"members_new"}});
Индексы
Индексация поддерживает эффективное выполнение запросов. Без индексов MongoDB необходимо сканировать каждый документ коллекции для выбора тех документов, которые соответствуют запросу. Данный процесс крайне неэффективен и требует обработки большого количества данных.
Индексы MongoDB – это специальные структуры данных, которые хранят небольшие части данных в форме, которая легко распознаётся. Они хранят значение определённого поля или набора полей, упорядоченных по значению поля, указанному в индексе.
Построим индекс по ключу bid
:
db.band.ensureIndex({bid: 1}) // deprecated db.users.createIndex({"name" : 1}) // actual
ensureIndex
устарел, начиная с версии 3.0, в данный момент является псевдонимом для db.collection.createIndex()
.
Полезные материалы: на metanit.com/nosql/mongodb — Работа с индексами,
proselyte.net/tutorials/mongodb/indexing — Индексация в MongoDB
Схемы и модели
Схемы определяют структуру документов внутри коллекции, а модели используются для создания копий данных, хранящихся в документах.
Основы MongoDB
- 1 Внутри MongoDB может быть ноль или более баз данных.
- 2 База данных может иметь ноль или более «коллекций» (коллекция практически тоже что и таблица).
- 3 Коллекции состоят из нуля или более «документов». Опять же, документ можно рассматривать как «строку».
- 4 Документ состоит из одного или более «полей», которые — как можно догадаться — подобны «колонкам».
- 5 «Индексы» в MongoDB почти идентичны таковым в реляционных базах данных.
- 6 Важно понимать, что когда мы запрашиваем у MongoDB какие-либо данные, то она возвращает курсор, с которыми мы можем делать все что угодно.
Отличия MongoDB от реляционных БД
Основное различие в том, что реляционные базы данных определяют «колонки» на уровне «таблицы», в то время как документ-ориентированные базы данных определяют «поля» (в реляционных «колонки») на уровне «документа» (в релационных «запись»).
В конечном счёте дело в том, что коллекция не содержит информации о структуре содержащихся в ней данных. Информацию о полях содержит каждый отдельный документ.
Селекторы запросов
Селектор запросов MongoDB (это JSON-объект) аналогичен предложению where
SQL-запроса. Как таковой он используется для поиска, подсчёта, обновления и удаления документов из коллекций.
Селектор — это JSON-объект, в простейшем случае это может быть даже {}
, что означает выборку всех документов (аналогичным образом работает null
). Если нам нужно выбрать всех единорогов (англ. «unicorns») женского рода, можно воспользоваться селектором {gender:'f'}
.
{поле: значение}
используется для поиска всех документов, у которых есть ‘поле’ и у него есть ‘значение’.
{поле1: значение1, поле2: значение2}
работает как логическое И
.
Оператор $lt, $lte, $gt, $gte, $ne
Специальные операторы $lt
, $lte
, $gt
, $gte
и $ne
используются для выражения операций «меньше», «меньше или равно», «больше», «больше или равно», и «не равно».
Пример использовани селекторов с командой find
(но также селекторы могут быть использованы с remove
, count
, update
):
Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать:
db.unicorns.find({gender: 'm', weight: {$gt: 700}})
Оператор $exists
Оператор $exists
используется для проверки наличия или отсутствия поля, например:
db.unicorns.find({vampires: {$exists: false}})
Оператор $or
Оператор $or
используется как ИЛИ
db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]}) //... или любят яблоки, или любят апельсины, или весят менее 500 фунтов
Отметьте
db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), loves: ['apple', 'watermelon'], weight: 601, gender: 'f', vampires: 33});
Поле loves
это массив. MongoDB поддерживает массивы как объекты первого класса. Самое интересное это та простота, с которой делается выборка по значению массива: {loves: 'watermelon'}
вернёт нам все документы, у которых watermelon
является одним из значений поля loves
.
Оператор $where
Оператор $where
(в след. разделах)
Самый гибкий оператор — $where
, позволяющий нам передавать JavaScript для его выполнения на сервере.
Оператор ObjectId
ObjectId
, сгенерированный MongoDB для поля _id
, подставляется в селектор следующим образом:
db.unicorns.find({_id: ObjectId("TheObjectId")})
update
В простейшей форме, update
принимает 2 аргумента: селектор для выборки и то, чем обновить соответствующее поле. Второй параметр используется для полной замены оригинала:
db.unicorns.update({name: 'Roooooodles'}, {weight: 590})
По умолчанию, update
обновляет лишь первый найденный документ
Модификатор $set
Модификатор $set
обновляет конкретные поля, а не весь документ.
Если вам нужно всего лишь изменить пару полей, лучше всего использовать модификатор $set
:
db.unicorns.update({weight: 590}, {$set: {name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], gender: 'm', vampires: 99}}) db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})
Не забывайте использовать модификатор $set
, если вам нужно обновить лишь некоторые поля.
модификатор $inc
Модификатор $inc
— увеличить или уменьшить значение поля. Модификатор воздействуют непосредственно на поля, а не на весь документ.
db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})
Модификатор $push
Модификатор $push
— позволяет добавить данные в массив. Модификатор воздействуют непосредственно на поля, а не на весь документ.
db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})
Разрешаем вставку при обновлении (3-й параметр)
Обновление/вставка: обновляет документ, если он найден, или создаёт новый — если не найден. Чтобы разрешить вставку при обновлении (если элемент не будет найден), установите третий параметр в true
.
db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}); db.hits.find();
Вставки и обновления не будет, так как 3-й параметр опущен, а документа с {page: 'unicorns'}
отсутствует в коллекции.
db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true); db.hits.find();
Поскольку документы с полем page
, равным unicorns
, не существуют, то будет создан новый документ. Если выполнить это вторично, существующий документ будет обновлён, и поле hits
увеличится до 2.
Одновременно создастся коллекция hits
, если она отсутствует.
Множественные обновления (4-й параметр)
Чтобы обновить множество документов нужно установить четвертый параметр в true
:
db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);
Этим мы обновили все поля добавив везде поле vaccinated
со значением true
find (курсор)
Курсор базы данных — это объект БД, который позволяет приложениям работать с записями «по-одной», а не с множеством сразу. То есть курсор (как мы помним это объект), который позволяет передвигаться по выборке (назад на одно, вперед на одну, в конец/начало) при помощи своих методов.
Как уже упоминалось, результатом find
является курсор. Второй необязательный параметр у find
это список полей, которые мы хотим получить.
db.unicorns.find(null, {name: 1}); cursor = db.unicorns.find(null, {name: 1});
_id
по умолчанию возвращается всегда. Но мы можем исключить _id
следующим образом: {name:1, _id: 0}
.
Получаем все поля, кроме поля name
:
db.unicorns.find({}, {name: 0})
Как уже упоминалось, результатом find
является курсор. Поэтому мы можем присоединить к нему ряд методов:
Сортировка (метод sort)
Синтаксис метода sort:
мы указываем поля, по которым надо сортировать, используя 1
для сортировки по возрастанию и -1
для сортировки по убыванию. Например:
db.unicorns.find().sort({weight: -1}) // по убыванию 999,998,997 ...
db.unicorns.find({}, {name: true}).sort({name: -1})
Но для сортировки большого объема данных в MongoDB необходимо использовать индексы.
Метод limit()
db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3)
Метод skip()
Метод skip()
позволяет пропустить определенное количество записей.
db.unicorns.find({}, {name: true}).sort({name: -1}).limit(3).skip(1)
Обратите внимание как мы соединяем методы в цепочки.
Моделирование данных
MongoDB не поддерживает JOIN
. По существу мы должны делать второй запрос, чтобы найти связанные данные.
p.s: Для создания нового ObjectID
используется следующий код: NewObjectId = ObjectId()
Моделируем ‘один-ко-многим’ или ‘многие-ко-многим’
Когда требуется смоделировать отношения «один-ко-многим» или «многие-ко-многим» можно использовать массивы ( в MongoDB массивы это объекты первого класса).
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona', manager: [ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })
При этом следующий find
сработает:
db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})
Массивы значений намного удобнее в использовании, нежели таблицы связи «многие-ко-многим»
Вложенные документы
MongoDB поддерживает вложенные документы:
db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})
Вложенные документы можно запрашивать с помощью точечной нотации:
db.employees.find({'family.mother': 'Chani'})
Денормализация
Традиционный путь ассоциировать пользователя с его постом — это колонка userid
в таблице posts
. С такой моделью нельзя отобразить список постов без дополнительного извлечения данных (JOIN) из таблицы пользователей. Возможное решение — хранить имя пользователя (name
) вместе с userid
для каждого поста.
Команды (выжимка)
db.version() //показывает номер версии сервера
db.getCollectionNames() //получить список коллекций внутри нашей БД
db.unicorns.find() //вернет список документов (записей)
db.unicorns.remove() //поскольку мы не передали селектора, произойдёт удаление всех документов
//Например, чтобы получить всех самцов единорога, весящих более 700 фунтов, мы можем написать: db.unicorns.find({gender: 'm', weight: {$gt: 700}})
//Оператор $exists используется для проверки наличия или отсутствия поля, например: db.unicorns.find({vampires: {$exists: false}})
//Оператор $or используется как ИЛИ db.unicorns.find({gender: 'f', $or: [{loves: 'apple'}, {loves: 'orange'}, {weight: {$lt: 500}}]})
//update принимает 2 аргумента: селектор (where) для выборки и то, чем обновить соответствующее поле. //Второй параметр используется для полной замены оригинала db.unicorns.update({name: 'Roooooodles'}, {weight: 590})
//Модификатор $set обновляет конкретные поля, а не весь документ db.unicorns.update({name: 'Roooooodles'}, {$set: {weight: 590}})
//модификатор $inc - увеличить или уменьшить значение поля db.unicorns.update({name: 'Pilot'}, {$inc: {vampires: -2}})
//модификатор $push - позволяет добавить данные в массив db.unicorns.update({name: 'Aurora'}, {$push: {loves: 'sugar'}})
//Обновление/вставка обновляет документ, если он найден, или создаёт новый — если не найден. //Чтобы разрешить вставку при обновлении, установите третий параметр в true (ниже мы создаем коллекцию hits, если ее нет) db.hits.update({page: 'unicorns'}, {$inc: {hits: 1}}, true);
//если установить 4-й параметр в true, то обновятся все документы db.unicorns.update({}, {$set: {vaccinated: true }}, false, true);
//Второй необязательный параметр у find указывает на список полей, которые мы хотим получить. db.unicorns.find(null, { name:true })
//получаем все поля, кроме поля name: db.unicorns.find({}, {name: 0})
//сортировка по убыванию db.unicorns.find().sort({weight: -1})
// сортируем по весу, но получаем 2 и 3 по весу единорога, пропуская 1-го db.unicorns.find().sort({weight: -1}).limit(2).skip(1)
//подсчитать кол-во единорогов на счету которых более 60 вампиров db.unicorns.count({vampires: {$gt: 50}})
//Когда требуется смоделировать отношения «один-ко-многим» или «многие-ко-многим» можно использовать массивы db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d733"), name: 'Siona', manager: [ObjectId("4d85c7039ab0fd70a117d730"), ObjectId("4d85c7039ab0fd70a117d732")] })
// Ищем значение в массиве manager db.employees.find({manager: ObjectId("4d85c7039ab0fd70a117d730")})
//MongoDB поддерживает вложенные документы: db.employees.insert({_id: ObjectId("4d85c7039ab0fd70a117d734"), name: 'Ghanima', family: {mother: 'Chani', father: 'Paul', brother: ObjectId("4d85c7039ab0fd70a117d730")}})
//Вложенные документы можно запрашивать с помощью точечной нотации: db.employees.find({'family.mother': 'Chani'})
// версия > 3.2 db.employees.updateOne(…); db.employees.updateMany (…);
operator update (документация)
// другие команды для работы с коллекциями: insertOne insertMany // получаем кол-во документов в коллекции db.cats.count() // модификаторы: // длина массива равен 3 {$size: 3} // выборка по типу {$type: number}
Подключение MongoDB в Node.js
// Retrieve var MongoClient = require('mongodb').MongoClient; // Connect to the db MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) { if(!err) { console.log("We are connected"); } });
Mongoose
ODM – Object-Document Mapper (объектно-документное отображение). У MongoDB нет жесткой структуры, а вот Mongoose позволяет нам ввести понятие схемы.
Установка и подключение (работаем через mongoose)
//install $ npm install mongoose
//use var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/test'); var db = mongoose.connection; db.on('error', console.error.bind(console, 'connection error:')); db.once('open', function() { // connect });
Установка и подключение (работаем через нативный клиент — MongoClient)
var MongoClient = require("mongodb").MongoClient; mongoClient.connect("mongodb://localhost:27017/test", function(err, db){ if(err){ return console.log(err); } // работаем с БД db.close(); });
Схема
Схема в Mongoose определяет метаданные модели — ее свойства, типы данных и ряд другой информации. mongoosejs.com/docs/guide.html
var mongoose = require('mongoose'); var Schema = mongoose.Schema; // внутри перечисляем наши поля var blogSchema = new Schema({ title: String, author: String, body: String, comments: [{ body: String, date: Date }], date: { type: Date, default: Date.now, required: [true, 'Укажите дату'] }, hidden: Boolean, meta: { votes: Number, favs: Number } }); var Blog = mongoose.model('Blog', blogSchema); // ready to go!
Типы схем
mongoosejs.com/docs/schematypes.html
Модель
Модели (http://mongoosejs.com/docs/models.html) — это конструкторы, составленные из определения нашей схемы. Экземпляры модели представляют собой документы, которые могут быть сохранены и извлечены из нашей БД.
var schema = new mongoose.Schema({ name: 'string', size: 'string' }); var Tank = mongoose.model('Tank', schema);
Первый параметр в методе mongoose.model
указывает на название модели, а второй параметр — схема.
Сохраняем объект в БД
Кроме метода save()
также можно использовать метод Person.create()
(см. код ниже). Первый параметр метода — сохраняемый объект.
var Person = mongoose.model('Person', yourSchema); var subject = new Person({name: 'John'}); subject.save(function(err) { if (err) return handkeError(err); }) // или Person.create({name: 'John'}, function(err, subject) { if (err) return handkeError(err); })
Поиск (find, findById, findOne)
http://mongoosejs.com/docs/queries.html
Методы Для получения данных:
find
— возвращает все объекты, которые соответствуют условию фильтрации. find()
в качестве первого параметра принимает условие фильтрации; второй параметр метода find()
— функция обратного вызова, в которую передаются полученные из БД документы. Если в качестве условия фильтрации передаются пустые фигурные скобки ({}
), то возвращаются все объекты.
findById
— возвращает один объект по значению поля _id
. Метод возвращает документ с определенным идентификатором.
findOne
— возвращает один объект, который соответствует критерию фильтрации. В отличие от метода find
, метод findOne()
возвращает один объект.
var Person = mongoose.model('Person', yourSchema); // { 'name.last': 'Ghost' } - условие // 'name occupation' - выбираем нужные поля // function (err, person) { ... - обрабатываем данные в callback'е // find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function (err, person) { if (err) return handleError(err); console.log('%s %s is a %s.', person.name.first, person.name.last, person.occupation) // Space Ghost is a talk show host. }) // находим все Person.find({ 'name.last': 'Ghost' }, 'name occupation', function (err, docs) {}) // альтернатива callback Person.find({ 'name.last': 'Ghost' }, 'name occupation').exec(function (err, docs) {})
Редактирование (update, findByIdAndUpdate)
Каждая модель имеет метод update()
, который позволяет обновить документы в БД. Первый параметр метода — условие фильтрации. Второй параметр описывает, что и как надо изменить. В функцию обратного вызова передается результат операции.
Нередко для обновления используется фильтрация по _id
. И на этот случай мы можем использовать метод findByIdAndUpdate()
.
Первый параметр метода findByIdAndUpdate()
— значения для поля _id
у обновляемого документа, а второй — набор новых значений для полей объекта. В функцию обратного вызова передается обновленный документ.
// меняем Karl на Johny var query = {name: 'Karl'}; Model.update(query, {name: 'Johny'}, options, callback); // все заменит на {name: 'Johny'} Model.update(query, {$set: {name: 'Johny'}}, options, callback); // меняем только поле name Person.findOne({name: 'Johny'}, function (err, person) { if (err) return handleError(err); person.name = 'Johny'; person.save(); })
Удаление (remove, findOneAndRemove)
Для удаления применяется метод remove()
. В метод remove()
передается критерий фильтрации документов на удаление. Объект, который передается в функцию обратного вызова, содержит информацию об операции удаления.
Метод findOneAndRemove()
позволяет удалить один документ. В функцию обратного вызова метод findOneAndRemove()
передается удаленный документ.
И частная разновидность этого метода — удаление по полю _id
в виде метода findByIdAndRemove()
.
// удаление: Model.remove({name: 'Johny'}, function (err, person) { if (err) return handleError(err); })
Валидация в Mongoose
Mongoose
имеет ряд встроенных правил валидации, которые следует указывать в схеме:
required
— обязательно наличие значения для свойства.min и max
— задают минимальное и максимальное значения для числовых данных.minlength
иmaxlength
— задают минимальную и максимальную длину для строк.enum
— строка должна представлять одно из значений в указанном массиве строк.match
— строка должна соответствовать регулярному выражению.
Если мы попытаемся добавить некорректные данные в БД, то запрос на добавление вернет ошибку.
Пример:
const personScheme = new Schema({ name: { type: String, required: true, minlength:3, maxlength:15 }, age: { type: Number, required: true, min: 1, max:125 } });
Promise и Mongoose
С MongoDB можно использовать Promise
.
С помощью метода then мы можем получить данные, которые возвратил нам сервер и выполнить обработку результата.
user.save() .then(function(data){ console.log("Сохранен объект", data); mongoose.disconnect(); // отключение от базы данных }) .catch(function (err){ console.log(err); mongoose.disconnect(); });
mlab.com
mlab.com — это облачный сервис по предоставлению БД mongoDB.
- Создаем БД
- Добавляем пользователя
- И подключаемся в своем приложении:
mongoose .connect(`mongodb://user_name:pass@ds147072.mlab.com:47072/name_bd`);
CRUD
CRUD — (create, read, update, delete — «создание, чтение,обновление, удаление») — 4 основные функции, используемые при работе базами данных.
Послесловие
Node.js, а значит и Mongo, применяется для высоконагруженных проектов, там, где необходимо передавать много информации, чаты, игры.