Skip to content

Хранилища данных в Node.js: MongoDB

21/04/2010

MongoDB — документо-ориентированная база данных, ориентированная на кластеризацию и написанная на C++. Сами разработчики утверждают, что она находится как раз посередине между key-value stores и традиционными реляционными БД. Я решил её покопать после того как увидел на How to Node пост о создании блога на основе Express + MongoDB.

Документы в Mongo хранятся в виде JSON-подобных объектов, так что в JavaScript с ними работать довольно удобно. Кстати, для предварительного изучения MongoDB есть вот такая интерактивная веб-консоль со встроенным tutorial.

Установка

Выбираем версию на странице релизов, качаем wget‘ом и распаковываем:

  wget http://downloads.mongodb.org/linux/mongodb-linux-i686-1.4.1.tgz
  tar -xvzf mongodb-linux-i686-1.4.1.tgz
  

Создадим каталог для хранения файлов баз MongoDB:

  mkdir ~/mongo-data
  

Теперь запускаем

  cd mongodb-linux-i686-1.4.1/bin
  ./mongod --dbpath ~/mongo-data/ &
  

Сервер должен запуститься в виде фонового процесса и сразу предупредить нас что размер базы данных ограничен двумя гигабайтами (если Вы используете 32-хбитную сборку). Чтобы проверить его работу, сразу к нему подключимся:

  ./mongo
  

Должна открыться консоль MongoDB. Можно например набрать show dbs и увидеть список доступных баз.

По умолчанию MongoDB не запускается вместе с системой, если Вам это нужно, добавьте её самостоятельно в init.d. Я рассчитываю просто поизучать, поэтому пока этим заниматься не буду.

В качестве коннектора воспользуемся node-mongodb-native от christkv. Забираем архив последней версии со страницы загрузок:

  wget http://github.com/christkv/node-mongodb-native/tarball/V0.7.1
  tar -xvzf christkv-node-mongodb-native-V0.7.1-0-g08527ba.tar.gz
  mv christkv-node-mongodb-native-V0.7.1-0-g08527ba node-mongodb-native-V0.7.1
  

Папку с коннектором я переименовал исключительно для удобства использования. В папке будет Makefile, но он используется только для запуска тестов. Нас же интересует сам коннектор — он лежит в lib/mongodb.

Использование

В принципе к коннектору прилагается директория examples, содержащая аж 11 примеров работы с MongoDB, причём довольно интересных. Я тут приведу только самые базовые операции.

Открытие базы данных:

sys = require("sys");
test = require("mjsunit");

var mongo = require('../lib/mongodb');

// Хост и порт берутся из переменных окружения
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : mongo.Connection.DEFAULT_PORT;

sys.puts("Connecting to " + host + ":" + port);
var db = new mongo.Db('node-mongo-examples', new mongo.Server(host, port, {}), {});
db.open(function(err, db) {
    // Подключились к базе
});
  

Создание коллекции и добавление элементов:

  // Открываем коллекцию. Если её не существует, она будет создана
  db.collection('test', function(err, collection) {
    // Добавляем три элемента
    for(var i = 0; i < 3; i++) {
      collection.insert({'a':i});
    }
  });

  

Показ элементов из коллеции:

        collection.count(function(err, count) {
          sys.puts("There are " + count + " records in the test collection. Here they are:");

          // Получаем все элементы коллекции с помощью find()
          collection.find(function(err, cursor) {
            cursor.each(function(err, item) {

              // Null обозначает последний элемент
              if (item != null) {
                sys.puts(sys.inspect(item));
              } else {
                sys.puts("That's all!");
              }
            });
          });
        });
  

В MongoDB проходить по элементам коллекции можно с помощью итератора, необязательно доставать элементы по одному, как это приходится делать в CouchDB.

Курсор нужен для того чтобы запрашивать объекты из MongoDB по мере необходимости. Т.е., новый документ будет получен только после того как будет вызван each()/nextObject()/toArray().

Можно также производить поиск по коллекции, доставая только элементы с нужными свойствами:

  db.collection('test', function(err, collection) {
      collection.insert({'name':'Robert', 'age': 12});
      collection.insert({'name':'Agatha', 'age': 20});
      collection.insert({'name':'Sam', 'age': 6});

      // Получаем все элементы с age = 6
      collection.find({'age': 6}, function(err, cursor) {

        // Преобразовываем их в массив
        cursor.toArray(function(err, items) {
            // items - массив документов с age = 6
        });
      });
  });

  

Удаление документов из коллекции:

  db.collection('test', function(err, collection) {
    // Удаляем элементы с age = 20
    collection.remove({'age': 20}, function(err, collection) {

        // Удаляем все элементы
        collection.remove(function(err, collection) {
            // Все элементы удалены
        });

    })
  });
  

При сохранении документа в MongoDB ему назначается _id, как и в CouchDB. Но в отличии от Couch, в Mongo удобнее оперировать документами с помощью свойств.

Кроме этого, в MongoDB есть много других интересных вещей: хранение двоичных файлов, ссылки на документы в других коллекциях (примерный аналог — foreign key в SQL, насколько я понял), поддержка индексов. MongoDB выглядит очень удобной базой данных для приложений разной сложности. Полагаю, следующее мини-приложение я напишу именно с её помощью, чтобы лучше освоиться с базой.

Ссылки по теме

Реклама
12 комментариев
  1. Сергей, не могли бы вы пояснить вот эти моменты:

    > В MongoDB проходить по элементам коллекции можно с помощью итератора, необязательно доставать элементы по одному, как это приходится делать в CouchDB.

    и

    > При сохранении документа в MongoDB ему назначается _id, как и в CouchDB. Но в отличии от Couch, в Mongo удобнее оперировать документами с помощью свойств.

    PS: оставил тот же комментарий на nodejs.ru — не сразу понял что там не оригинал.

    • 1. Мы указываем что нам нужны определённые элементы, и мы хотим их обработать. Мы навешиваем обработчик с помощью each, и он достаёт элементы по одному и обрабатывает. Примерно как each в jQuery. Плюс в том что нам не нужно ждать пока загрузится весь набор элементов (а он может быть очень большим), мы обрабатываем их в потоковом режиме. Заодно и память экономится. Each проходит по всем элементам самостоятельно, но можно это делать вручную с помощью nextObject.

      2. У документа есть свойство _id, и есть другие, «второстепенные» свойства. Вот например мы сохраняем HTML-страницы. Вместе с каждой страницей мы сохраняем её текст и URL. В CouchDB мы не можем достать страницу по «второстепенному» свойству — например, с определённым URL, нам надо обязательно указывать _id документа. А в Mongo мы можем делать выборку по любому из свойств — по _id, по URL и т.д. В CouchDB можно конечно задавать пользовательские _id — например, использовать URL, но мы всё равно ограничены только одним свойством, которое идентифицирует записи.

      Зато в CouchDB удобнее делать «аггрегирующие» запросы благодаря Map/Reduce и хранению промежуточных результатов.

      • zit permalink

        добрый день, а можете поподробнее о «потоковом режиме» рассказать:
        1. все ли операции выполняются асинхронно?
        2. в туториалах (и интерактивной веб-консоле) работа с тем же find много проще — как раз из-за блокировки до получения данных?

        еще мне очень интересно, имеет ли ли право на жизнь такой подход: при инициниализации считываем (можно и синхронно) все элементы коллекции и «перегоняем» в js-массив, при изменении js-объектов (которые в массиве) вызываем асинхронный save/update вообще без callback’а?

        заранее спасибо

        ps не напишете русским языком, как ссылаться на объекты из других коллекций? 🙂

        • Save/update без callback’ов вполне возможен, и в принципе достаточно распространен когда надо просто сохранить данные. Да, все операции насколько я знаю асинхронны, это немного усложняет код (это можно немного исправить библиотекой вроде Do).

          Способ с массивом в принципе вполне имеет право на жизнь (можно сохранять данные при апдейте с помощью сеттеров).

          Про ссылки на объекты из других коллекций могу посмотреть завтра, как доберусь до сервера.

      • zit permalink

        и забыл 🙂 в монго injecting, как в sql, при исользовании json’а в принципе не возможен?

        • Injecting думаю вряд ли возможен — команда и данные там всё таки разделены, JSON формируется встроенными средствами V8.

    • Nodejs.ru я тоже просматриваю, но нечасто. У моих статей там внизу ссылка на этот блог, здесь я отвечаю быстрее.

  2. Про итерацию так и не понял, почему в CouchDB нужно что-то доставать по одному. Что мне мешает так же брать по чуть-чуть и итерироваться.

    > А в Mongo мы можем делать выборку по любому из свойств – по _id, по URL и т.д. В CouchDB можно конечно задавать пользовательские _id – например, использовать URL, но мы всё равно ограничены только одним свойством, которое идентифицирует записи.

    Так сделав в CouchDB индекс вида:

    emit(doc.url, doc);

    можно тоже делать запросы по урлу. Конечно это лишнее движение, но всё-равно достаточно просто.

    • 1. В MongoDB для этого есть нативный интерфейс, в CouchDB придётся писать свой. Но да, можно.

      2. Здесь мы получим *все* документы с их урлами. И потом нам придётся их перебрать. Что если мне нужны документы с урлом «example.com»? Или собранные 1 апреля 2010 года? Во вьюшки же нельзя передавать параметры, насколько я понимаю.

      • Поправка — похоже, запрос по определённому свойству сделать можно, если объявить соответствующую вьюшку.

Trackbacks & Pingbacks

  1. Хранилища данных в Node.js: MongoDB « nodeJS

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: