понедельник, 22 августа 2011 г.

Haskell и MongoDB. Updated.


Как все мы знаем, Haskell - это быстроменяющийся и быстроразвивающийся язык программирования. Совсем недавно вышла новая версия компилятора. А если у языка так быстро развивается его сердце, то что уж говорить о его библиотеках.

Вот так и произошло. Не так давно я написал статью об использовании MongoDB и Haskell, а она уже не актуальна. Ничего не поделаешь, надо усп.


Эта статья будет намного короче, т.к. новая версия модуля позволяет нам избежать некоторое количество boilerplate. А также я не буду вдаваться в некоторые подробности, поэтому я посоветовал бы прочесть и старую статью.

Сразу импортируем модуль MongoDB и включаем нужные нам Language Extensions:
{-# LANGUAGE OverloadedStrings, ExtendedDefaultRules #-}
import Database.MongoDB
import Control.Monad.IO.Class
import Network.Socket (HostName)

Для подключения к базе данных, необходимо создать pipe:
-- для стандартного порта
newPipe :: HostName -> IO Pipe
newPipe = runIOE . connect . host

-- с возможностью указать порт
newPipe' h = runIOE . connect . Host h . PortNumber

Отличие от старой версии в том, что функция connect устанавливает подключение сразу, поэтому если вы задали неправильные параметры, либо демон MongoDB отключен, вы сразу об этом узнаете, т.к. будет вызван exception.

Для выполнения запросов мы сделаем короткий алиас:
dbName   = "database_name" :: Database
run' :: MonadIO m => Pipe -> Action m a -> m (Either Failure a)
run' pipe = access pipe master dbName

Функция run' принимает на вход pipe и сам Action. Конечно же, указывать имя базы данных явно - не лучшее решение. Поэтому мы можем записать эту функцию так:
run' pipe dbName = access pipe master dbName

Функция run' - общая функция, с помощью нее можно производить как вставку, так и получение информации.
Для получения BSON-документа из БД (грубо говоря, для select'a), мы сделаем более узкую функцию:
dbRecv :: Pipe -> Action IO Cursor -> IO (Either Failure [Document])
dbRecv pool a = run' pool (a >>= rest)

Для вставки в базу данных можно использовать функцию run', либо сделать "эстетический" хелпер:
dbSend pipe = run' pipe

Особого смысла в нем нет, но проще воспринимается и легче запоминается его название.

Как вы уже заметили, вместе с OverloadedStrings мы также включили ExtendedDefaultRules. В прошлый раз вместо него мы использовали функции i и u, для явного указания типа.

Теперь нам это совершенно не нужно, и мы можем писать такой запрос для выборки пользователей, с автомобилем BMW:
find (select ["car" =: "BMW"] "users_collection") {
             sort    = ["_id" =: -1]
           , project = ["username" =: 1, "age" =: 1]
           }

Вместо:
find (select ["car" =: u"BMW"] "users_collection") { 
             sort    = ["_id" =:  i (-1)] 
           , project = ["username" =: i 1, "age" =: i 1]
           }
-- или вместо
find (select ["car" =: u"BMW"] "users_collection") { 
             sort    = ["_id" =: -1 :: Int] 
           , project = ["username" =: 1 :: Int, "age" =: 1 :: Int]
           }
Что, конечно же, просто замечательно.

Статью я буду периодически обновлять, ибо разработчик модуля MongoDB для Haskell постоянно ведет работу над ним. Вы можете следить за его работой на GitHub.

Успехов!

Комментариев нет:

Отправить комментарий