mongodb/tutorial.md
2010-03-09 18:36:15 -06:00

5 KiB

MongoDB Haskell Mini Tutorial

Author: Brian Gianforcaro (b.gianfo@gmail.com)

Updated: 2/28/2010

This is a mini tutorial to get you up and going with the basics of the Haskell mongoDB drivers. It is modeled after the pymongo tutorial.

You will need the mongoDB bindings installed as well as mongo itself installed.

$ = command line prompt
> = ghci repl prompt

Installing Haskell Bindings

From Source:

$ git clone git://github.com/srp/mongoDB.git
$ cd mongoDB
$ runhaskell Setup.hs configure
$ runhaskell Setup.hs build
$ runhaskell Setup.hs install

From Hackage using cabal:

$ cabal install mongoDB

Getting Ready

Start a MongoDB instance for us to play with:

$ mongod

Start up a haskell repl:

$ ghci

Now We'll need to bring in the MongoDB/BSON bindings:

> import Database.MongoDB
> import Database.MongoDB.BSON

Making A Connection

Open up a connection to your DB instance, using the standard port:

> con <- connect "127.0.0.1" []

or for a non-standard port

> import Network
> con <- connectOnPort "127.0.0.1" (Network.PortNumber 666) []

By default mongoDB will try to find the master and connect to it and will throw an exception if a master can not be found to connect to. You can force mongoDB to connect to the slave by adding SlaveOK as a connection option, eg:

> con <- connect "127.0.0.1" [SlaveOK]

Databases, Collections and FullCollections

As many database servers, MongoDB has databases--separate namespaces under which collections reside. Most of the APIs for this driver request the FullCollection which is simply the Database and the Collection concatenated with a period.

For instance 'myweb_prod.users' is the the FullCollection name for the *Collection 'users' in the database 'myweb_prod'.

Databases and collections do not need to be created, just start using them and MongoDB will automatically create them for you.

In the below examples we'll be using the following FullCollection:

> import Data.ByteString.Lazy.UTF8
> let testcol = (fromString "test.haskell")

You can obtain a list of databases available on a connection:

> dbs <- databaseNames con

You can obtain a list of collections available on a database:

> collections <- collectionNames con (fromString "test")

Documents

Data in MongoDB is represented (and stored) using JSON-style documents. In mongoDB we use the BsonDoc type to represent these documents. At the moment a BsonDoc is simply a tuple list of the type '[(ByteString, BsonValue)]'. Here's a BsonDoc which could represent a blog post:

> import Data.Time.Clock.POSIX
> now <- getPOSIXTime
> :{
  let post = [(fromString "author", BsonString $ fromString "Mike"),
              (fromString "text",
               BsonString $ fromString "My first blog post!"),
              (fromString "tags",
               BsonArray [BsonString $ fromString "mongodb",
                          BsonString $ fromString "python",
                          BsonString $ fromString "pymongo"]),
              (fromString "date", BsonDate now)]
  :}

With all the type wrappers and string conversion, it's hard to see what's actually going on. Fortunately the BSON library provides conversion functions toBson and fromBson for converting native between the wrapped BSON types and many native Haskell types. The functions toBsonDoc and fromBsonDoc help convert from tuple lists with plain String keys, or Data.Map.

Here's the same BSON data structure using these conversion functions:

> :{
  let post = toBsonDoc [("author", toBson "Mike"),
                        ("text", toBson "My first blog post!"),
                        ("tags", toBson ["mongoDB", "Haskell"]),
                        ("date", BsonDate now)]
  :}

Inserting a Document

> insert con testcol post

Getting a single document with findOne

> findOne con testcol (toBsonDoc [("author", toBson "Mike")])

Querying for More Than One Document

> cursor <- find con testcol (toBsonDoc [("author", toBson "Mike")])
> allDocs cursor

You can combine these into one line:

> docs <- allDocs =<< find con testcol (toBsonDoc [("author", toBson "Mike")])

See nextDoc to modify cursor incrementally one at a time.

  • Note: allDocs automatically closes the cursor when done, through nextDoc.

Counting

We can count how many documents are in an entire collection:

> num <- count con testcol

Or we can query for how many documents match a query:

> num <- countMatching con testcol (toBsonDoc [("author", toBson "Mike")])

Range Queries

No non native sorting yet.

Indexing

WIP - coming soon.

Something like...

> index <- createIndex con testcol [("author", Ascending)] True