{"id":21902516,"url":"https://github.com/jmininger/ocaml-mongodb-driver-tutorial","last_synced_at":"2025-06-28T17:36:00.298Z","repository":{"id":240404754,"uuid":"186158638","full_name":"jmininger/OCaml-MongoDB-driver-tutorial","owner":"jmininger","description":"A tutorial with code samples for the MongoML driver for OCaml","archived":false,"fork":false,"pushed_at":"2019-05-15T20:45:53.000Z","size":10,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-15T20:14:16.786Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jmininger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-11T16:50:53.000Z","updated_at":"2020-06-01T02:10:49.000Z","dependencies_parsed_at":"2024-05-18T16:41:17.066Z","dependency_job_id":null,"html_url":"https://github.com/jmininger/OCaml-MongoDB-driver-tutorial","commit_stats":null,"previous_names":["jmininger/ocaml-mongodb-driver-tutorial"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jmininger/OCaml-MongoDB-driver-tutorial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmininger%2FOCaml-MongoDB-driver-tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmininger%2FOCaml-MongoDB-driver-tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmininger%2FOCaml-MongoDB-driver-tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmininger%2FOCaml-MongoDB-driver-tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jmininger","download_url":"https://codeload.github.com/jmininger/OCaml-MongoDB-driver-tutorial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jmininger%2FOCaml-MongoDB-driver-tutorial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262470239,"owners_count":23316462,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-28T15:19:29.382Z","updated_at":"2025-06-28T17:36:00.270Z","avatar_url":"https://github.com/jmininger.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MongoML tutorial: An OCaml driver for MongoDB\n\n### Introduction\n[MongoML][1] is currently the only OCaml driver for MongoDB. It uses \n[BSON](http://bsonspec.org/) to communicate via tcp/ip sockets to the MongoDB \nserver. This means that it relies on OCaml's built-in [_Unix module_](https://caml.inria.fr/pub/docs/manual-ocaml/libref/Unix.html)\n(although most of the Unix api seems to work on Windows, according to the docs).  \n\n#### Async vs Sync API\nCommands in the _Mongo_ API are synchronous, but there is a Mongo\\_lwt package\ninside of module in the [MongoML Repo][2] that wraps all _Mongo_ functions\nin a Lwt monad. This module does not appear to come in the opam package.  \n  \n#### APIs\nHere is the documentation for the three APIs that we will be using:\n  - [Mongo](http://massd.github.io/mongo/doc/Mongo.html)\n  - [MongoAdmin](http://massd.github.io/mongo/doc/MongoAdmin.html)\n  - [Bson](http://massd.github.io/bson/doc/Bson.html)\n\nIts a pretty small api, and the Bson functions are particularly worth looking\nat.  \n\n\n### Installation\nMongoML does not work on ocaml versions \u003e than 4.05.0, so the best solution is\nto create an opam switch.\n\n```\n$ mkdir MongoTutorial\n$ cd MongoTutorial\n$ opam switch create Mongo ocaml.4.05.0\n$ opam install mongo\n```\n\nNow, any opam commands in the directory (and all directories lower in the\nhierarchy) will be using the switch environment instead of the default opam\nenvironment. \n\nMake sure that a mongod instance is running: \n```\n$ mongod\n```\n\n**One more thing**: I strongly reccomend downloading \n[MongoCompass](https://docs.mongodb.com/compass/master/install/), a Desktop UI\nthat allows you to see the changes that you make to your MongoDB.\n\n### Basic functions\nLets begin by creating a collection. In MongoDB, _collections_ are the NoSQL\nanalogs of SQL tables, and _documents_ are SQL row analogs.  \n\n```ocaml\n(* val create : string -\u003e int -\u003e string -\u003e string -\u003e t *)\n\nlet myMongoDB = \n  let open Mongo in\n  let url = \"127.0.0.1\" in (* string *)\n  let port = 27017 in (* int *)\n  let dbName = \"TutorialDB\" in\n  let collectionName = \"newCollection\" in\n  try \n    create url port dbName collectionName\n  with \n    Mongo_failed ex -\u003e Pervasives.print_endline ex\n```\n\nHere the _create_ function connects to the db and creates a `Mongo.t` which \npoints to the collection and is needed as an argument for almost\nevery other function in the API.  \n\nIf the `TutorialDB` database already exists, you will receieve a pointer to\nthat database--there is no error to indicate that the db already exists (the\nsame goes for the collection argument)\n\nAlso in this function is the `Mongo.Mongo_failed` exception type that contains\na string. This is the only exception in the Mongo module, and many functions\nraise it if an error occurs. \n\n```ocaml\nlet () = \n  destory myMongoDB (* spelled \"de-story\" not \"destroy\" *)\n```\nAfter use, a `Mongo.t` should be destroyed to preserve resources.  \n\n**Important:** the spelling is **correct** in the code sample above. It is an unfortunate\nspelling error that the current MongoML repo has corrected, but that correction\nis lacking the version that opam distributes. It has been\nlike this for at least two years. If the opam version is ever upgraded, it will\nlikely cause breaking changes.   \n\n### Inserting Elements\n\n```ocaml\ntype person = { name: string; age: int; hobbies: string list }\n\nlet person_to_bson {name; age; hobbies} = \n  let hobby_elems = List.map (Bson.create_string) hobbies in\n  Bson.empty \n    |\u003e Bson.add_element \"name\" (Bson.create_string name)\n    |\u003e Bson.add_element \"age\" (Bson.create_int64 (Int64.of_int age))\n    |\u003e Bson.add_element \"hobbies\" (Bson.create_list hobby_elems)\n\nlet bson_to_person bson = \n  let name = \n    Bson.get_element \"name\" bson\n      |\u003e Bson.get_string in\n  let age = \n     Bson.get_element \"age\" bson\n      |\u003e Bson.get_int64 \n      |\u003e Int64.to_int in\n  let hobbies =\n    Bson.get_element \"hobbies\" bson\n      |\u003e Bson.get_list \n      |\u003e List.map (Bson.get_string) in\n  {name; age; hobbies }\n\n(** Main: Insert 5 documents into mongodb and print any exceptions that are thrown *)\nlet () =\n  let people = [\n        {name=\"John Doe\"; age=21; hobbies=[\"Soccer\"; \"Coding\"]};\n        {name=\"Jane Smith\"; age=25; hobbies=[\"Drawing\"]};\n        {name=\"Person1\"; age=10; hobbies=[\"philosophy\"; \"reading\"]};\n        {name=\"Person2\"; age=20; hobbies=[\"surfing\"; \"volleyball\"]};\n        {name=\"Person3\"; age=30; hobbies=[\"coding\"]}\n      ] \n  in \n  try \n      (* val insert : t -\u003e Bson.t list -\u003e unit *)\n    List.map person_to_bson people\n      |\u003e Mongo.insert myMongoDB\n\n  with \n    Mongo.Mongo_failed ex -\u003e (\n      Pervasives.print_endline ex;\n      Mongo.destory myMongoDB\n  )\n```\n\nTo create Bson objects, we start with an empty objects and add key value pairs,\nusing the `Bson.add_element` function. \n\n### BSON   \n\nThe following is all available Bson constructors: \n```ocaml\nval create_double : float -\u003e element\nval create_string : string -\u003e element\nval create_doc_element : t -\u003e element\nval create_list : element list -\u003e element\nval create_user_binary : string -\u003e element\nval create_objectId : string -\u003e element\nval create_boolean : bool -\u003e element\nval create_utc : int64 -\u003e element\nval create_null : unit -\u003e element\nval create_regex : string -\u003e string -\u003e element\nval create_jscode : string -\u003e element\nval create_jscode_w_s : string -\u003e t -\u003e element\nval create_int32 : int32 -\u003e element\nval create_int64 : int64 -\u003e element\nval create_minkey : unit -\u003e element\nval create_maxkey : unit -\u003e element\n```\nEach function above has a corresponding *get* function (ex. string: `get_string`, list: `get_list`) \nthat takes a BSON element and returns an OCaml type.   \n\nAlso important are the `get_element`, `add_element`, and `remove_element` functions that provide \noperations for interacting with `Bson.t` types.   \n\nHere are their signatures: \n```ocaml\nval add_element : string -\u003e Bson.element -\u003e Bson.t -\u003e Bson.t\n\nval get_element : string -\u003e Bson.t -\u003e Bson.element\n\nval remove_element : string -\u003e Bson.t -\u003e Bson.t\n```\n   \n\n### Query Objects and Selector Objects\n\nQueries in MongoDB are specified using BSON objects called _query objects_.\nWe can make a query object that grabs all documents from our database with an\n`age \u003e= 21` or with `name = \"Person1\"` like this:\n```ocaml\nlet query = \n  Bson.empty\n    |\u003e Bson.add_element \"$or\" \n      (Bson.empty\n        |\u003e Bson.add_element \"age\" \n            (Bson.create_doc_element \n              (Bson.add_element \"$gte\" \n              (Bson.create_int64 (Int64.of_int 21)) Bson.empty))\n        |\u003e Bson.add_element \"name\" \n          (Bson.create_doc_element \n            (Bson.add_element \"$eq\" \n            (Bson.create_string \"Person1\") Bson.empty))\n      )\n```\n\nWe can also make sure that only certain columns from documents are returned by\nusing a _selector object_.  \n\n```ocaml\nlet selector = Bson.empty\n  |\u003e Bson.add_element \"name\" (Bson.create_boolean true)\n  |\u003e Bson.add_element \"age\" (Bson.create_boolean true)\n```\n\nIf passed as an argument with the query object above, we get the NoSQL equivalent\nof: \n```sql\nSELECT name, age, _id\nFROM myMongoDB\nWHERE age \u003e= 21 or name = \"Person1\"\n```\n\n### Queries and MongoReply\n\nEach query returns a MongoReply, which consists of a header, flags, num_items_returned, \na cursor_id, and a document list. To see what a MongoReply looks like use `MongoReply.to_string` \nto return a printable string.\nWe can use `MongoReply.get_document_list` to get a list of documents that are returned.    \n\n\n**Note:** In the MongoML api, only functions ending with *\"of_num\"* return cursors\n\n\n```ocaml\n(* Get all documents *)\nlet all_docs = \n  Mongo.find myMongoDB \n    |\u003e MongoReply.get_document_list\n\n(* Get first doc *)\nlet first_doc = \n  Mongo.find_one myMongoDB \n    |\u003e MongoReply.get_document_list\n\n(* Get docs matching the query *)\nlet matching_docs = \n  Mongo.find_q query_obj\n    |\u003e MongoReply.get_document_list\n\n(* Get docs matching the query, with only the specified attributes *)\nlet matching_docs = \n  Mongo.find_q_s query_obj selector_obj\n    |\u003e MongoReply.get_document_list\n```\nNote: I forego the obligatory `try-with` blocks in the above examples  \n\nThere are some more subtle variations of the above query funcs that can be found \n[here](http://massd.github.io/mongo/doc/Mongo.html)  \n\n### Cursors\nA cursor_id acts like an iterator, holding a reference to a particular spot in the \ncollection. In the MongoML api, only functions ending with *\"of_num\"* return cursors.\n\n```ocaml\n(* Get the first two docs *)\nlet reply = Mongo.find_of_num myMongoDB 2 in\nlet cursor = MongoReply.get_cursor reply in\n\n(* Get the next doc *)\nlet newReply = Mongo.get_more_of_num cursor 1\nlet next_cursor = MongoReply.get_cursor newReply\n\n(* Get the rest of the documents *)\nlet rest = Mongo.get_more next_cursor\n...\n\nMongo.kill_cursors myMongoDB [cursor; next_cursor]\n```  \nThe  `kill_cursors` method is meant to help MongoDB save resources when your are done with \nthe cursors.   \n\n**Note:** the specific case for `Mongo.find_of_num someDB 1`, where 1 is the integer passed\nin, does not return a cursor, like one might expect.\n\n### Deleting Entries\n```ocaml\n val delete_one : t -\u003e Bson.t -\u003e unit\n\n val delete_all : t -\u003e Bson.t -\u003e unit\n```\n```ocaml\n(* Selects docs with: name == \"John Doe\" *)\nlet delete_selector = \n  Bson.empty\n  |\u003e  Bson.add_element \"name\" \n    (Bson.create_doc_element \n      (Bson.empty \n        |\u003e Bson.add_element \"$eq\" (Bson.create_string \"John Doe\"))\n    )\n\nlet _ = \n  delete_one myMongoDB delete_selector\n\n(* Selects docs with names containing \"Person\" *)\nlet people_prefix_selector = \n  Bson.empty \n  |\u003e Bson.add_element \"name\" (Bson.create_doc_element (\n      Bson.empty \n      |\u003e Bson.add_element \"$regex\" (Bson.create_string \"Person\")\n    ))\n\nlet _ = \n  Mongo.delete_all myMongoDB people_prefix_selector\n\n(* \n  To delete all the elements in a collection without a selector, \n   pass in an empty Bson object: \n*)\nlet _  = \n  Mongo.delete_all myMongoDB (Bson.empty)\n\n```\n### Updates and Update Objects\n\nUpdates work pretty much the same as queries and deletes, with the exception that updates\ntake an additional object that describe how to update documents matching the selection object.\n\n```ocaml\nval update_one : t -\u003e Bson.t * Bson.t -\u003e unit\n\nval update_all : t -\u003e Bson.t * Bson.t -\u003e unit\n```\nEach of the functions above take a pair of BSON values, the first representing \nthe selector document, the second, representing the update document\n\n```ocaml\n(* Take a name, and increase the age by one in all documents with a matching name *)\nlet birthday name =\n  let select_doc = \n    Bson.empty |\u003e \n      Bson.add_element \"name\" (Bson.create_doc_element (\n        Bson.empty |\u003e Bson.add_element \"$eq\" (Bson.create_string name))) in\n  let update_doc = \n    Bson.empty |\u003e Bson.add_element \"$inc\" (Bson.create_int64 1) in\n  Bson.update_all myMongoDB (select_doc, update_doc)\n```\n\n\n### The MongoAdmin Module\nMongoAdmin provides a set of useful tools for managing a db cluster.  \n\nThe MongoAdmin module does not require that you specify a DB and Collection to \ncreate an object --only a port and a uri.\n\nSee the [API](http://massd.github.io/mongo/doc/MongoAdmin.html) for more information\n```ocaml\nval create : string -\u003e int -\u003e t\n\nval destory : t -\u003e unit\n\n\nval listDatabases : t -\u003e MongoReply.t\nval buildInfo : t -\u003e MongoReply.t\nval collStats : t -\u003e MongoReply.t\nval connPoolStats : t -\u003e MongoReply.t\nval cursorInfo : t -\u003e MongoReply.t\nval getCmdLineOpts : t -\u003e MongoReply.t\nval hostInfo : t -\u003e MongoReply.t\nval listCommands : t -\u003e MongoReply.t\nval serverStatus : t -\u003e MongoReply.t\n```  \n\n### Potential Projects\n  - Create ppx syntax extensions to convert records to bson and vice versa\n  - Create a query-object builder function\n  - Figure out how to hack the MongoML project to add other mongo ops like authentication\n  - Figure out how to use the Lwt module of the library\n\n\n[1]:http://massd.github.io/mongo/\n[2]:https://github.com/MassD/mongo\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmininger%2Focaml-mongodb-driver-tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjmininger%2Focaml-mongodb-driver-tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjmininger%2Focaml-mongodb-driver-tutorial/lists"}