Advertisement
  1. Code
  2. Coding Fundamentals
  3. Databases & SQL

Отображение реляционных баз данных и SQL в MongoDB

Scroll to top
Read Time: 11 min

() translation by (you can also view the original English article)

Базы данных NoSQL значительно выросли за последние несколько лет, привлекая людей своей структурой, гибким дизайном схемы и быстрым доступом к данным по сравнению с реляционными базами данных. MongoDB является документно-ориентированной базой данных NoSQL с открытым исходным кодом, которая хранит данные в форме JSON-объектов. Она стала одной из ведущих баз данных из-за своей динамичный схемы, хорошей масштабируемости, оптимальной производительности выполнения запросов, быстрее индексации и активного сообщества пользователей.

Если вы пришли из мира RDBMS/SQL, то понимание концепций NoSQL и MongoDB может стать немного сложным, так как обе технологии имеют совсем разные подходы к представлению данных. Эта статья будет руководством для вас на пути понимания, каким образом RDBMS/SQL и его функционал и язык запросов может быть отображен в базе данных MongoDB. Под отображением я понимаю то, что если у нас есть определенная концепция в RDBMS/SQL, то мы сможем увидеть эквивалент ее в MongoDB.

Мы начнем наше отображение с базовых реляционных концепций, таких как таблица, запись, колонка и т.д., а так же индексы и связи. Затем мы рассмотрим SQL запросы и обсудим соответствующие им запросы в MongoDB. Данная статья подразумевает, что у вас есть базовые знания основных реляционных концепций SQL, так как на протяжении статьи будет сделан упор на то, как эти концепции будут отображены в MongoDB. Начнем.


Отображение таблиц, строк и колонок.

Каждая база данных в MongoDB состоит из коллекций, которые эквиваленты тому, как в RDBMS базы данных состоят из SQL таблиц. Каждая коллекция хранит данные в виде документов, каждый из которых эквивалентен тому, как таблицы хранят данные в строках. В то время как строка хранит данные в виде набора колонок, документ представляет собой JSON-структуру (известную как BSON в MongoDB). И точно так же как у нас есть столбцы в SQL строке, есть поля в MongoDB. Это пример документа (читаем строки) с несколькими полями (колонками) с данными пользователя:

1
{
2
"_id": ObjectId("5146bb52d8524270060001f3"),
3
"age": 25,
4
"city": "Los Angeles",
5
"email": "mark@abc.com",
6
"user_name": "Mark Hanks"
7
}

Этот документ соответствует одной строке в RDBMS. Коллекция состоит из множества таких документов точно так же, как таблица состоит из множества строк. Обратите внимание, что каждый документ содержит уникальное поле _id, которе является 12-байтовым и служит в качестве первичного ключа у документа. Это поле генерируется автоматически во время создания документа и используется для добавления уникальности каждому документу.

Чтобы лучше понять отображение, рассмотрим пример из SQL таблицы users и соответствующую структуру в MongoDB. Как показано на Рис 1, каждая строка в SQL таблице трансформируется в документ, а каждая колонка в поле в MongoDB.

Figure 1 Mapping Table to Collection (1)Figure 1 Mapping Table to Collection (1)Figure 1 Mapping Table to Collection (1)
Рисунок 1

Динамическая схема

Стоит обратить внимание на одну интересную способность документов в коллекции - они могут иметь разные схемы. Таким образом в MongoDB один документ может иметь пять полей, а другой - семь. Поля можно легко добавить или удалить в любое время. Так же нет никаких ограничений на тип данных поля. Таким образом в одном документе поле может иметь данные типа int, а в другом содержать array.

Эти концепции могут показаться совсем необычными для тех читателей, кто имеет за собой RDBMS прошлое, где структуры таблиц, колонки и типы данных всегда заранее определены. Такая возможность использования динамической схемы позволяет нам на лету генерировать различные документы.

К примеру рассмотрим следующие два документа внутри одной коллекции, но которые имеют разные схемы (Рис 2):

Figure 2 Documents in a Collection having different structureFigure 2 Documents in a Collection having different structureFigure 2 Documents in a Collection having different structure
Рисунок 2

В первом документе есть поля address и dob, которых нет во втором документе, в то время как второй документ содержит поля gender и occupation, которых нет в первом. Представьте, что нам бы пришлось реализовать это в SQL, нам бы понадобилось хранить четыре дополнительных колонки для address, dob, gender и occupation, некоторые из которых бы имели пустые значения, и тем самым занимали бы ненужное место.

Такая модель динамической схемы - причина того, что NoSQL базы данных очень гибкие в рамках дизайна схемы. Различные сложные схемы (иерархии, древовидные структуры и прочее), которые потребовали несколько таблиц RDBMS, могут быть эффективно реализованы в такого рода документах. Типичным примером может могут служить посты, их лайки, комментарии и любая другая ассоциируемая с ними информация. SQL реализация этого в идеале бы имела несколько разных таблиц для хранения постов, комментариев и лайков, в то время как MongoDB документ может хранить всю эту информацию в одном документе.


Отображение связей и слияний

Связи в RDBMS получаются с помощью первичного и внешнего ключей, а так же запросов к ним с использованием слияний. Не существует прямого сопоставления связей в MongoDB, но здесь связи получаются с помощью использования вложенных документов или указанием ссылок на документы.

Рассмотрим пример, где нам нужно хранить данные пользователя, а так же соответствующую контактную информацию. В идеальной SQL схеме у нас будет две таблицы, допустим user_information и contact_information, с первичными ключами id и contact_id, как это показано на Рис 3. Таблица contact_information также будет содержать колонку user_id, которая будет внешним ключом, указывающим на поле id из таблицы user_information.

Figure 3Figure 3Figure 3
Рисунок 3

Теперь рассмотрим то, как мы сможем проектировать эти связи в MongoDB, используя подход связывания документов и вложенных документов. Обратите внимание, что в схеме SQL, мы обычно добавляем столбец (например, id и contact_id в нашем случае), который выступает в качестве основного столбца для этой таблицы. Однако в MongoDB, мы обычно используем автоматически сгенерированное поле _id в качестве первичного ключа, чтобы однозначно определить документ.

Связывание документов

Данный подход использует две коллекции, user_information и contact_information, обе из которых имеют уникальные поля _id. У нас будет поле user_id в документе contact_information, который указывает на поле _id из документа user_information, показывая на какую контактную информацию он ссылается. (Смотрим Рис 4) Обратите внимание, что в MongoDB нужно аккуратно относится ко связям и операциям над ними, так как нет никаких ограничений и правил на внешний ключ.

Figure 4 Linking Documents in MongoDBFigure 4 Linking Documents in MongoDBFigure 4 Linking Documents in MongoDB
Figure 4

Поле user_id в нашем документе - это просто поле, содержащее некоторые данные, и вся логика, связанная с их интерпретацией, должна быть реализована нами. Например, даже если вы вставите ссылку в user_id в документ из contact_information на несуществующий документ из коллекции user_information, то в MongoDB не возникнет никакой ошибки, что соответствующая запись с этим user_id в коллекции user_information не была найдена (в то время как в SQL это будет невалидный внешний ключ).

Вложенные документы

Вторым вариантом можно вложить документ contact_information внутрь документа user_information следующим образом (Рис 5):

Figure 5 Embedding Documents in MongoDBFigure 5 Embedding Documents in MongoDBFigure 5 Embedding Documents in MongoDB
Рисунок 5

В примере выше мы вложили небольшой документ с контактной информацией внутрь данных пользователя. Таким простым способом можно вкладывать друг в друга сложные документы или иерархические структуры.

Так же, выбор того или иного подхода (ссылка или встроенный документ) зависит от вашей ситуации. Если ожидается, что вложенные данные будут со временем увеличиваться в размере, то лучше использовать ссылку на документ, чтобы избежать слишком больших размеров документа. Вложенность обычно используется в случаях, когда количество встраиваемых данных ограничено (например адрес).


Диаграмма отражения

Чтобы подитожить данная диаграмма (Рис 6) представляет собой обсуждаемые раннее связи:

Figure 6 Mapping ChartFigure 6 Mapping ChartFigure 6 Mapping Chart
Рисунок 6

Перенос запросов SQL в MongoDB

Теперь когда мы чувствуем себя спокойно с базовыми возможностями MongoDB, следует обсудить язык запросов, который используется для взаимодействия с базой данных.

Для запросов MongoDB рассмотрим коллекцию users со следующей структурой документа:

1
{
2
"_id": ObjectId("5146bb52d8524270060001f3"),
3
"post_text":"This is a sample post" ,
4
"user_name": "mark",
5
"post_privacy": "public",
6
"post_likes_count": 0
7
}

Для запросов SQL мы будем использовать таблицу users и пять колонок следующей структуры:

Figure 7 Sample SQL TableFigure 7 Sample SQL TableFigure 7 Sample SQL Table
Рисунок 7

Мы рассмотрим запросы для создания и изменения коллекций (или таблиц), вставки, чтения, обновления и удаления документов (или строк). Мы будем рассматривать по одному запросу с каждой стороны, один - SQL, а другой для MongoDB. При пояснении запросов MongoDB, я предполагаю, что вы уже достаточно знакомы с синтаксисом SQL запросов. Запросы MongoDB, представленные здесь, написаны в среде Mongo JavaScript, в то время как SQL запросы набраны в MySQL.

Создание

В MongoDB нет необходимости явно создавать коллекцию и ее структуру (как например мы делаем для таблиц с запросом CREATE TABLE). Структура документа автоматически создается при первой вставке в коллекцию. Однако можно создать пустую коллекцию, используя команду createCollection.

1
2
SQL: CREATE TABLE `posts` (`id` int(11) NOT NULL AUTO_INCREMENT,`post_text` varchar(500) NOT NULL,`user_name` varchar(20) NOT NULL,`post_privacy` varchar(10) NOT NULL,`post_likes_count` int(11) NOT NULL,PRIMARY KEY (`id`))
3
4
MongoDB: db.createCollection("posts")

Вставка

Чтобы вставить документ в MongoDB, мы используем метод insert, который принимает пары ключ-значение. Вставленный документ будет содержать автоматически сгенерированное поле _id. Однако вы также можете явно предоставить значение 12-байтовое значение как _id вместе с другими полями.

1
2
SQL: INSERT INTO `posts` (`id` ,`post_text` ,`user_name` ,`post_privacy` ,`post_likes_count`)VALUES (NULL ,  'This is a sample post',  'mark',  'public',  '0');
3
4
MongoDB:  db.posts.insert({user_name:"mark", post_text:"This is a sample post", post_privacy:"public", post_likes_count:0})

В MongoDB нет функции Alter Table для изменения структуры документа. Так как документы в схеме являются динамическими, то схема меняется тогда, когда происходит обновление документа.

Чтение

MongoDB использует метод find, который является эквивалентом команды SELECT в SQL. Следующие выражения просто читают все документы из коллекции posts.

1
2
SQL: SELECT * FROM  `posts`
3
4
MongoDB: db.posts.find()

Следующий запрос выполняет поиск по условию для документов с полем user_name и значением mark. Все условия для выборки документов находятся внутри первых фигурных скобок {}, разделенные запятыми.

1
2
SQL: SELECT * FROM `posts` WHERE `user_name` =  'mark'
3
4
MongoDB: db.posts.find({user_name:"mark"})

Следующий запрос выбирает определенные колонки, post_text и post_likes_count, так как это определено вторым набором в фигурных скобках {}.

1
2
SQL: SELECT  `post_text` ,  `post_likes_count` FROM  `posts`
3
4
MongoDB: db.posts.find({},{post_text:1,post_likes_count:1})

Обратите внимание что по умолчанию MongoDB возвращает поле _id с каждым найденным документом. Если нам не нужно это поле в результирующем наборе, то необходимо явно это указать в виде ключа _id и значения 0 в списке возвращаемых колонок. Значение 0 означает, что мы хотим убрать это поле из результирующего набора.

1
2
MongoDB: db.posts.find({},{post_text:1,post_likes_count:1,_id:0})

Следующий запрос выбирает определенные поля и использует условия для выборки, что user_name равно mark.

1
2
SQL: SELECT  `post_text` , `post_likes_count` FROM `posts` WHERE `user_name` =  'mark'
3
4
MongoDB: db.posts.find({user_name:"mark"},{post_text:1,post_likes_count:1})

Теперь мы добавим еще одно условие для выборки публичных постов. Условия, разделенные запятыми, представляют собой логическое AND условие. Таким образом это выражение будет искать документы, имеющие user_name равное mark и post_privacy равное public.

1
2
SQL: SELECT  `post_text` ,  `post_likes_count` FROM  `posts` WHERE  `user_name` =  'mark' AND  `post_privacy` =  'public'
3
4
MongoDB: db.posts.find({user_name:"mark",post_privacy:"public"},{post_text:1,post_likes_count:1})

Чтобы использовать логическое OR между условиями в методе find, мы используем оператор $or.

1
2
SQL: SELECT  `post_text` ,  `post_likes_count` FROM  `posts` WHERE  `user_name` =  'mark' OR  `post_privacy` =  'public'
3
4
MongoDB: db.posts.find({$or:[{user_name:"mark"},{post_privacy:"public"}]},{post_text:1,post_likes_count:1})

Затем мы используем метод sort, который сортирует результаты по возрастанию поля post_likes_count.

1
2
SQL: SELECT *  FROM `posts` WHERE `user_name` = 'mark' order by post_likes_count ASC
3
4
MongoDB: db.posts.find({user_name:"mark"}).sort({post_likes_count:1})

Чтобы отсортировать результаты в обратном порядке мы передаем -1 как значение поля, по которому сортируем.

1
2
SQL: SELECT *  FROM `posts` WHERE `user_name` = 'mark' order by post_likes_count DESC
3
4
MongoDB: db.posts.find({user_name:"mark"}).sort({post_likes_count:-1})

Чтобы ограничить количество документов в ответе, можно использовать метод limit и явно указать число возвращаемых документов.

1
2
SQL: SELECT *  FROM `posts` LIMIT 10
3
4
MongoDB: db.posts.find().limit(10)

Точно так же как мы используем offset в SQL чтобы пропустить определенное число записей, мы можем использовать функцию skip в MongoDB. Например, следующее выражение выберет десять постов и пропустит первые пять.

1
2
SQL: SELECT *  FROM `posts` LIMIT 10 OFFSET  5
3
4
MongoDB: db.posts.find().limit(10).skip(5)

Изменение

Первым параметром в методе update мы передаем условия для выборки документов. Вторым параметром мы определяем то, каким образом будет выполнена операция изменения документа. Например, следующий запрос выберет все документы с user_name равным mask и установит поле post_privacy равным private.

Единственное различие, что по умолчанию в MongoDB update запрос обновит только один (первый который совпадет) документ. Чтобы обновить все документы, удовлетворяющие условию, нужно установить третий параметр multi в значение true, указывая на то, что мы хотим обновить сразу несколько документов.

1
SQL: UPDATE posts SET post_privacy = "private" WHERE user_name='mark'
2
3
MongoDB: db.posts.update({user_name:"mark"},{$set:{post_privacy:"private"}},{multi:true})

Удаление

Удаление документов довольно простая процедура и очень похожа на ее аналог в SQL.

1
2
SQL: DELETE FROM posts WHERE user_name='mark'
3
 
4
MongoDB:  db.posts.remove({user_name:"mark"})

Индексы

MongoDB по умолчанию имеет индекс на поле _id в каждой коллекции. Чтобы создавать индексы, можно использовать метод ensureIndex, указав поля и какой порядок сортировки будет использоваться 1 или - 1 (по возрастанию или убыванию).

1
2
SQL: CREATE INDEX index_posts ON posts(user_name,post_likes_count DESC)
3
4
MongoDB: db.posts.ensureIndex({user_name:1,post_likes_count:-1})

Чтобы просмотреть все индексы, которые есть в коллекции, можно использовать метод getIndexes, точно так же как мы используем SHOW INDEX в SQL.

1
2
SQL: SHOW INDEX FROM posts
3
4
MongoDB: db.posts.getIndexes()

Заключение

В этой статье мы рассмотрели элементарные концепции и термины RDBMS/SQL и их реализацию в MongoDB. Мы так же обратили внимание на проектирование связей в MongoDB и научились, как переносить базовые SQL запросы на язык запросов MongoDB.

Ознакомившись с этой статьей, вы можете дальше продолжить изучение более сложных запросов, включая агрегирование данных, map-reduce, а так же запросы, которые затрагивают несколько коллекций. Вы так же можете воспользоваться различными онлайн инструментами для конвертации SQL запросов в аналогичные им запросы MongoDB. Можете потренироваться в проектировании своей простой схемы базы MongoDB. Лучшим примером для этого может служить база данных для хранения постов, лайков, комментариев и лайков в комментариям. Это даст вам практический взгляд на всю гибкость в дизайне схемы, которую предлагает MongoDB.

Приветствую любые предложения, вопросы или идеи от вас в комментариях.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.