Meta
Доброго времени суток!
В данный момент я работаю над достаточно большим проектом, состоящим из нескольких модулей, и использующий разные технологии. Но сам сайт, а точнее его back-end написан целиком на Node.js, а Riak является основным хранилищем. Ничего не буду писать про сам Riak, на хабре и так есть отличная обзорная статья.
Как и для любой другой NoSQL базы данных, чтобы интегрировать функциональность БД в Node.js вам необходимо использовать драйвер или клиент этой базы данных, кому как нравится называть. Вам это надо для удобства пользования и составления запросов к БД, конечно вы можете это делать и напрямую, используя незатейливую команду curl.
Сразу хочу оговориться, что клиенты или драйвера для различных NoSQL БД называют по-разному, я же буду говорить или как об ORM или как о клиенте конкретной ДБ. Кстати, имено так о себе и пишут в Riak-js репозитории:
Node.js client for Riak.
Вот некоторые, а возможно что и все Node.js клиенты для riak
- riak-js — используемый в нашем проекте
- Simpleriak
- Riak-PB — использует protobuff, может быть немого быстрее
Из-за незначительного опыта работы с последними двумя, сказать мне вообщем-то про них нечего, поэтому дальше речь пойдет только riak-js.
Немного основ
В принципе работать с Riak-js очень просто, как и с большинством других DB ORM. Достаточно легко и без всяких сложностей можно научится делать запросы к базе данных, а затем обрабатывать полученный ответ. Если вы раньше работали, например с mongoose ( MongoDB ORM для Node.js), то вам не составит никакого труда освоиться и с Riak-js.
Хранилище нам нужно чтобы мы могли туда что-нибудь положить, а потом, когда понадобиться, быстро найти и вернуть пользователю как можно скорее и в том виде в котором пользователь его сохранил.
Рассмотрим функцию get:
//users - корзина (bucket), аналог таблицы в реляционных БД //id - искомый id, можно и хардкодом. ID'шники в Riak выглядят так NrIKwuvHZmJNIoQc8PeP8s12ic4 //3 параметр - это конечно callback db.get('users', id, function(err, result){ console.log(result); })
В примере выше, result будет красивенький json файл:
{ name = { fname: 'Ivan', lname: 'Ivanov' }, age: '30', city: 'New York', hobbies: ['football', 'programming', 'reading old books'] }
Другой пример, на этот раз мы будем сохранять объект в базу данных. Хочу заметить, что сохранять мы можем что угодно и куда угодно. Key создается автоматически и по дефолту не входит в состав значения, но в данном примере я покажу как его можно сохранить, как ID для дальнейшего использования, когда вам понадобится где-то указывать ID объекта или иметь специальный идентификатор.
db.save('users', ' ' , result, function(err, user, meta){ if(err){ throw err; } user.id = meta.key; // Маленький хак чтобы сохранить key, как ID юзера. Объект meta тянет на отдельную статью, // и суть его я расскрывать здесь не буду db.save('users', user.id, user); });
Теперь в нашей базе, в корзине users, есть 2 практических одинаковых юзера
// Юзер которого мы доставали в первом примере, у которого id еще не является частью всего значения // key: 'NrIKwuvHZmJNIoQc8PeP8s12ic4' { name = { fname: 'Ivan', lname: 'Ivanov' }, age: "30", city: "New York", hobbies: ['football', 'programming', 'reading old books'] } // Наш сохраненный юзер, где мы сохраняем key в поля id, уже после того как создали юзера // Фактические мы его перезаписываем (update) { name = { fname: 'Ivan', lname: 'Ivanov' }, age: "30", city: "New York", hobbies: ['football', 'programming', 'reading old books'], id: "MFz6fqzxATUxelg69LrZjEysOCx" // meta.key }
Поиск
Ок, но что если нам нужно найти группу юзеров или сделать более серьезный запрос. То мы можем использовать search.find
Например давайте искать всех юзеров старше 18, которые живут в Нью-Йорке.
db.search.find('users', ' age > 18 AND city = "New York" ', function(err, users){ //Если надо вставить переменную, то делаем так : // db.search.find('users', ' age > 18 AND city = " ' + city + ' " ', function(err, users){ if(err){ throw err; } //Обратите внимание, что результатом нашего запроса будет массив из юзеров console.dir(users); });
Однако, если используя db.get или db.getAll мы получаем красивенькие json файлы, то теперь результат придет в немного измененном виде.
{ id: 'MHAHovXhEWq82HBWCcwyBdyQ6jA', // id юзера который удовлетворяет условиям поиска index: 'users', fields: { name_fname: 'Ivan', // так станет выглядить наш объект name name_lname: 'Ivanov', age: '30', city: 'New York', hobbies: 'football programming reading old books', // А так станет выглядить массив ['football', 'programming', reading old books''] }, { id: 'MHAHovXhEWq82HBWCcwyBdyQ6jA', // id 2-ого юзера который удовлетворяет условиям поиска ....
Как вы видите с таким файлом достаточно неудобно работать, например если вам надо отрендерить hobbies, то нормально это сделать путем простого перебора элементов не получаться, придется придумывать велосипеды типа split(‘ ‘) и это только в том случае если у вас нету сложных-составных элементов, как у меня в примере ‘reading old books’. В этом же случае, вам придется на стадии сохранения каждого хобби в массив заменять пробелы в составных элементах на "_" и только потом делать split(‘ ‘). А если у вас несколько вложенных объектов и несколько массивов в них? И вам надо подсчитать их длину? Тут придется браться за AJAX, что не всегда удобно и быстро. Стоит все же отметить, что даже делать второй отдельный запрос, чтобы узнать длину hobbies будет быстрее, чем совать AJAX функцию на страницу.
Чтобы этого избежать и не мучиться с корявым json файлов, лучше всего подойдет вместо db.search.find использовать MapReduce. Что это такое и о преимуществах использования данного метода вы можете прочитать во все той же статье, ссылку на которую я давал выше.
Справедливости ради стоит отменить, что такое кривое отображения файлы происходит не по вине riak-js, а из-за search-механизма самого Riak. Вы получите точно такой же результат, если будите запрашивать что-нибудь напрямую
curl "http://localhost:8098/solr/users/select?q=city:New*&wt=json"
Вернемся к MapReduce.
Давайте поищем тех самых юзеров из Нью-Йорка старше 18. Для это напишем простенькую функции к которой будем обращаться.
function getUsers(callback){ db.mapreduce.add('users') //название корзины .map('Riak.mapValuesJson') ..метод который используем на этапе map .run(function (err, data) { if (err) { throw err; } for (var i = 0; i < data.length; i++) { if(data[i].age > 18 && data[i].city.match('New York')){ console.dir(data[i]); } } callback(null, data); }); }
В результате мы получим неизмененный json файл,, содержащий всех юзеров, которые удовлетворяют условиям нашего поиска. Я считаю, что этот подход наиболее правильный и гибкий, по сравнению с db.search. Во-первых мы используем MapReduce, что само по себе является одной из причем почему был выбран именно Riak в качестве БД (учитывая, что все это потом будет запускаться именно на серверах amazon), а во-вторых вы имеете намного больше возможностей в составлении запросов, используя любимый всеми нами JavaScript.
Результатом будет массив из юзеров
[{ name = { fname: 'Ivan', lname: 'Ivanov' }, age: "30", city: "New York", hobbies: ['football', 'programming', 'reading old books'] // key: 'NrIKwuvHZmJNIoQc8PeP8s12ic4', нету id, так как мы его не сохраняли }, { name = { fname: 'Ivan', lname: 'Ivanov' }, age: "30", city: "New York", hobbies: ['football', 'programming', 'reading old books'], id: "MFz6fqzxATUxelg69LrZjEysOCx" // meta.key }, { name = { fname: 'Petr', lname: 'Petrov' }, age: "22", city: "New York", hobbies: ['hunting'], id: "UvCI0FwqvUK3I8ZzNh46IlylI2q" // meta.key }, ... ]
Так же интересную деталь вы можете прочитать и в доках кампании которая разрабатывает Riak. Следующий текст — это ответ на вопрос, когда использовать MapReduce.
docs.basho.com/riak/latest/tutorials/querying/MapReduce/#When-to-Use-MapReduce
When you want to return actual objects or pieces of the object – not just the keys, as do Search & Secondary Indexes
В вольном переводе: «Когда вы хотите получить непосредственно сам объект или его части, а не только ключи (id), как это происходит при Search или использовании Secondary Indexes»
Заключение
Riak — мощная и надежная БД, с которой при этом легко работать. Riak-js мог бы быть немного и лучше, но вцелом вся функциональность есть и пользоваться можно. Что касается поиска, то вместо search, лучше использовать MapReduce — это, то ради чего и создавался Riak, для хранения огромных массивов данных и быстрого поиска по ним используя свободные кластеры.
Ситуацию с поиском может поправить yokozuna, но как пишут сами разработчики. Yokozuno — это прототип и находится в стадии разработки, и не рекомендуется к использованию в данный момент.
ps.
Буду рад любой критике и комментариям.
ссылка на оригинал статьи http://habrahabr.ru/post/185254/
Добавить комментарий