{"id":13805537,"url":"https://github.com/elbywan/cryomongo","last_synced_at":"2025-04-09T18:22:06.326Z","repository":{"id":52222439,"uuid":"270114225","full_name":"elbywan/cryomongo","owner":"elbywan","description":"A MongoDB driver written in pure Crystal. ❄️","archived":false,"fork":false,"pushed_at":"2024-11-13T14:49:07.000Z","size":1643,"stargazers_count":72,"open_issues_count":1,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-02T12:38:57.091Z","etag":null,"topics":["crystal","mongo","mongodb","mongodb-driver"],"latest_commit_sha":null,"homepage":"https://elbywan.github.io/cryomongo/Mongo.html","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elbywan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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},"funding":{"github":"elbywan","custom":["https://www.paypal.me/elbywan"]}},"created_at":"2020-06-06T21:52:44.000Z","updated_at":"2025-01-29T03:35:15.000Z","dependencies_parsed_at":"2024-06-21T16:50:36.829Z","dependency_job_id":"89ff33ac-3224-4861-8d7c-816223b1b9c9","html_url":"https://github.com/elbywan/cryomongo","commit_stats":{"total_commits":125,"total_committers":2,"mean_commits":62.5,"dds":"0.016000000000000014","last_synced_commit":"02058a1cfb6fec4c9427200a4bc73f964544634d"},"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fcryomongo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fcryomongo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fcryomongo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fcryomongo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elbywan","download_url":"https://codeload.github.com/elbywan/cryomongo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248085662,"owners_count":21045194,"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":["crystal","mongo","mongodb","mongodb-driver"],"created_at":"2024-08-04T01:01:02.094Z","updated_at":"2025-04-09T18:22:06.300Z","avatar_url":"https://github.com/elbywan.png","language":"Crystal","funding_links":["https://github.com/sponsors/elbywan","https://www.paypal.me/elbywan"],"categories":["Database Drivers/Clients"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\t\u003cimg src=\"icon.svg\" width=\"128\" height=\"128\" /\u003e\n\t\u003ch1\u003ecryomongo\u003c/h1\u003e\n  \u003ch3\u003eA MongoDB driver written in pure Crystal.\u003c/h3\u003e\n  \u003ca href=\"https://github.com/elbywan/cryomongo/actions/workflows/specs.yml\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/elbywan/cryomongo/actions/workflows/specs.yml/badge.svg?branch=master\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/elbywan/cryomongo/tags\"\u003e\u003cimg alt=\"GitHub tag (latest SemVer)\" src=\"https://img.shields.io/github/v/tag/elbywan/cryomongo\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/elbywan/cryomongo/blob/master/LICENSE\"\u003e\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/github/license/elbywan/cryomongo\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\n\u003chr/\u003e\n\n#### Cryomongo is a high-performance MongoDB driver written in pure Crystal. (i.e. no C dependencies needed.)\n\n*Compatible with MongoDB 3.6+. Tested against: 4.2.*\n\n**⚠️ BETA state.**\n\n\u003e If you are looking for a higher-level object-document mapper library, you might want to check out the [`moongoon`](https://github.com/elbywan/moongoon) shard.\n\n## Installation\n\n1. Add the dependency to your `shard.yml`:\n\n```yaml\ndependencies:\n  cryomongo:\n    github: elbywan/cryomongo\n```\n\n2. Run `shards install`\n\n## Usage\n\n### Minimal working example\n\n```crystal\nrequire \"cryomongo\"\n\n# Create a Mongo client, using a standard mongodb connection string.\nclient = Mongo::Client.new # defaults to: \"mongodb://localhost:27017\"\n\n# Get database and collection.\ndatabase = client[\"database_name\"]\ncollection = database[\"collection_name\"]\n\n# Perform crud operations.\ncollection.insert_one({ one: 1 })\ncollection.replace_one({ one: 1 }, { two: 2 })\nbson = collection.find_one({ two: 2 })\nputs bson.not_nil!.[\"two\"] # =\u003e 2\ncollection.delete_one({ two: 2 })\nputs collection.count_documents # =\u003e 0\n```\n\n### Complex example with serialization\n\n```crystal\nrequire \"cryomongo\"\n\n# We take advantage of the BSON serialization capabilities provided by the `bson.cr` shard.\nrecord User,\n  name : String,\n  banned : Bool? = false,\n  _id : BSON::ObjectId = BSON::ObjectId.new,\n  creation_date : Time = Time.utc do\n  include BSON::Serializable\n  include JSON::Serializable\nend\n\n# Initialize Client, Database and Collection.\nclient = Mongo::Client.new\ndatabase = client[\"database\"]\nusers = database[\"users\"]\n\n# We set majority read and write at the Database level.\ndatabase.read_concern = Mongo::ReadConcern.new(level: \"majority\")\ndatabase.write_concern = Mongo::WriteConcern.new(w: \"majority\")\n\n# Drop and recreate the Collection to ensure that we read later only the documents we inserted in this example.\n{ Mongo::Commands::Drop, Mongo::Commands::Create }.each do |command|\n  database.command(command, name: \"users\")\nrescue e : Mongo::Error::Command\n  # ignore the server error, drop will fail if the collection has not been created before.\nend\n\n# Insert User structures that are automatically serialized to BSON.\nusers.insert_many({ \"John\", \"Jane\" }.map { |name|\n  User.new(name: name)\n}.to_a)\n\n# Fetch a Cursor pointing to the users collection.\ncursor = users.find\n\n# Iterate the cursor and use `.of(User)` to deserialize as the cursor gets iterated.\n# Then push the users into an array that gets pretty printed.\nputs cursor.of(User).to_a.to_pretty_json\n# =\u003e [\n#   {\n#     \"name\": \"John\",\n#     \"banned\": false,\n#     \"_id\": {\n#       \"$oid\": \"f2001c5fb0a33e0264e2ea05\"\n#     },\n#     \"creation_date\": \"2020-07-25T09:52:50Z\"\n#   },\n#   {\n#     \"name\": \"Jane\",\n#     \"banned\": false,\n#     \"_id\": {\n#       \"$oid\": \"f2001c5fb0a33e0264e2ea07\"\n#     },\n#     \"creation_date\": \"2020-07-25T09:52:50Z\"\n#   }\n# ]\n```\n\n## Features\n\n- **[CRUD operations](https://docs.mongodb.com/manual/crud/index.html)**\n- **[Aggregation](https://docs.mongodb.com/manual/aggregation/) (except: Map-Reduce)**\n- **[Bulk](https://docs.mongodb.com/manual/reference/method/Bulk/index.html)**\n- **[Read](https://docs.mongodb.com/manual/reference/read-concern/index.html) and [Write](https://docs.mongodb.com/manual/reference/write-concern/) Concerns**\n- **[Read Preference](https://docs.mongodb.com/manual/core/read-preference/index.html)**\n- **[Authentication](https://docs.mongodb.com/manual/core/authentication/index.html) (only: SCRAM mechanisms)**\n- **[TLS encryption](https://docs.mongodb.com/manual/core/security-transport-encryption/)**\n- **[Indexes](https://docs.mongodb.com/manual/indexes/index.html)**\n- **[GridFS](https://docs.mongodb.com/manual/core/gridfs/index.html)**\n- **[Change Streams](https://docs.mongodb.com/manual/changeStreams/index.html)**\n- **[Admin/Diagnostic commands](https://elbywan.github.io/cryomongo/Mongo/Commands.html)**\n- **[Tailable and Awaitable cursors](https://docs.mongodb.com/manual/core/tailable-cursors/index.html)**\n- **[Collation](https://docs.mongodb.com/manual/reference/collation/index.html)**\n- **Standalone, [Sharded](https://docs.mongodb.com/manual/sharding/) or [ReplicaSet](https://docs.mongodb.com/manual/replication/) topologies**\n- **[Command monitoring](https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst)**\n- **Retryable [reads](https://docs.mongodb.com/manual/core/retryable-reads/) and [writes](https://docs.mongodb.com/manual/core/retryable-writes/)**\n- **[Causal consistency](https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#client-sessions-and-causal-consistency-guarantees)**\n- **[Transactions](https://docs.mongodb.com/manual/core/transactions/)**\n\n## Conventions\n\n- Methods and arguments names are in **snake case**.\n- Object arguments can usually be passed as a **[NamedTuple](https://crystal-lang.org/api/NamedTuple.html)**, **[Hash](https://crystal-lang.org/api/Hash.html)**, **[BSON::Serializable](https://github.com/elbywan/bson.cr#serialization)** or a **[BSON](https://elbywan.github.io/bson.cr/BSON.html)** instance.\n\n## Documentation\n\n**The generated API documentation is available [here](https://elbywan.github.io/cryomongo/Mongo.html).**\n\n### Connection\n\n```crystal\nrequire \"cryomongo\"\n\n# Mongo::Client is the root object for interacting with a MongoDB deployment.\n# It is responsible for monitoring the cluster, routing the requests and managing the socket pools.\n\n# A client can be instantiated using a standard mongodb connection string.\n\n# Client options can be passed as query parameters…\nclient = Mongo::Client.new(\"mongodb://address:port/database?appname=MyApp\")\n# …or with a Mongo::Options instance…\noptions = Mongo::Options.new(appname: \"MyApp\")\nclient = Mongo::Client.new(\"mongodb://address:port/database\", options)\n# …or both.\n\n# Instantiate objects to interact with a specific database or a collection…\ndatabase   = client[\"database_name\"]\ncollection = database[\"collection_name\"]\n# …or using `default_database` if the connection uri string contains a default auth database component (\"/database\").\ndatabase   = client.default_database\ncollection = database.not_nil!.collection[\"collection_name\"]\n\n# The overwhelming majority of programs should use a single client and should not bother with closing clients.\n# Otherwise, to free the underlying resources a client must be manually closed.\nclient.close\n```\n\n```crystal\n# To enable SSL/TLS, use the `tls` option, alongside the `tlsCAFile` and `tlsCertificateKeyFile` options.\nuri = \"mongodb://localhost:27017/?tls=true\u0026tlsCAFile=./ca.crt\u0026tlsCertificateKeyFile=./client.pem\"\nssl_client = Mongo::Client.new uri\n```\n\n**Links**\n\n- [Mongo::Client](https://elbywan.github.io/cryomongo/Mongo/Client.html)\n- [Mongo::Options](https://elbywan.github.io/cryomongo/Mongo/Options.html)\n\n### Authentication\n\n*Cryomongo only supports the SCRAM-SHA1 and SCRAM-SHA256 authentication methods without SASLprep.*\n\n```crystal\nrequire \"cryomongo\"\n\n# To use authentication, specify a username and password when passing an URI to the client constructor.\n# Authentication methods depend on the server configuration and on the value of the `authMechanism` query parameter.\nclient = Mongo::Client.new(\"mongodb://username:password@localhost:27017\")\n```\n\n### Basic operations\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\n\n# Most CRUD operations are performed at collection-level.\ncollection = client[\"database_name\"][\"collection_name\"]\n\n# The examples below are very basic, but the methods can accept all the options documented in the MongoDB manual.\n\n## Create\n\n# Insert a single document\ncollection.insert_one({ key: \"value\" })\n# Insert multiple documents\ncollection.insert_many((1..100).map{|i| { count: i }}.to_a)\n\n# To track the _id, generate and pass it as a property\nid = BSON::ObjectId.new\ncollection.insert_one({ _id: id, key: \"value\" })\n\n## Read\n\n# Find a single document\ndocument = collection.find_one({ _id: id })\ndocument.try { |d| puts d.to_json }\n# Find multiple documents.\ncursor = collection.find({ qty: { \"$gt\": 4 }})\nelements = cursor.to_a # cursor is an Iterable(BSON)\n\n## Update\n\n# Replace a single document.\ncollection.replace_one({ name: \"John\" }, { name: \"Jane\" })\n# Update a single document.\ncollection.update_one({ name: \"John\" }, { \"$set\": { name: \"Jane\" }})\n# Update multiple documents\ncollection.update_many({ name: { \"$in\": [\"John\", \"Jane\"] }}, { \"$set\": { name: \"Jules\" }})\n# Find one document and replace it\ndocument = collection.find_one_and_replace({ name: \"John\" }, { name: \"Jane\" })\nputs document.try \u0026.[\"name\"]\n# Find one document and update it\ndocument = collection.find_one_and_update({ name: \"John\" }, { \"$set\": { name: \"Jane\" }})\nputs document.try \u0026.[\"name\"]\n\n## Delete\n\n# Delete one document\ncollection.delete_one({ age: 20 })\n# Delete multiple documents\ncollection.delete_many({ age: { \"$lt\": 18 }})\n# find_one_and_delete\ndocument = collection.find_one_and_delete({ age: { \"$lt\": 18 }})\nputs document.try \u0026.[\"age\"]\n\n# Aggregate\n\n# Perform an aggregation pipeline query\ncursor = collection.aggregate([\n  {\"$match\": { status: \"available\" }}\n  {\"$limit\": 5},\n])\ncursor.try \u0026.each { |bson| puts bson.to_json }\n\n# Distinct collection values\nvalues = collection.distinct(\n  key: \"field\",\n  filter: { age: { \"$gt\": 18 }}\n)\n\n# Documents count\ncounter = collection.count_documents({ age: { \"$lt\": 18 }})\n\n# Estimated count\ncounter = collection.estimated_document_count\n```\n\n**Links**\n\n- [Mongo::Collection](https://elbywan.github.io/cryomongo/Mongo/Collection.html)\n- [Mongo::Database](https://elbywan.github.io/cryomongo/Mongo/Database.html)\n\n### Bulk operations\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\n\n# A Bulk object can be initialized by calling `.bulk` on a collection.\ncollection = client[\"database_name\"][\"collection_name\"]\nbulk = collection.bulk\n# A bulk is ordered by default.\nbulk.ordered? # =\u003e true\n\n500.times { |idx|\n  # Build the queries by calling bulk methods multiple times.\n  bulk.insert_one({number: idx})\n  bulk.delete_many({number: {\"$lt\": 450}})\n  bulk.replace_one({ number: idx }, { number: idx + 1})\n}\n\n# Execute all the queries and return an aggregated result.\npp bulk.execute(write_concern: Mongo::WriteConcern.new(w: 1))\n```\n\n**Links**\n\n- [Mongo::Bulk](https://elbywan.github.io/cryomongo/Mongo/Bulk.html)\n\n### Indexes\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\ncollection = client[\"database_name\"][\"collection_name\"]\n\n# Create one index without options…\ncollection.create_index(\n  keys: {\n    \"a\":  1,\n    \"b\":  -1,\n  }\n)\n# or with options (snake_cased)…\ncollection.create_index(\n  keys: {\n    \"a\":  1,\n    \"b\":  -1,\n  },\n  options: {\n    unique: true\n  }\n)\n# and optionally specify the name.\ncollection.create_index(\n  keys: {\n    \"a\":  1,\n    \"b\":  -1,\n  },\n  options: {\n    name: \"index_name\",\n  }\n)\n\n# Follow the same rules to create multiple indexes with a single method call.\ncollection.create_indexes([\n  {\n    keys: { a: 1 }\n  },\n  {\n    keys: { b: 2 }, options: { expire_after_seconds: 3600 }\n  }\n])\n```\n\n**Links**\n\n- [Mongo::Collection](https://elbywan.github.io/cryomongo/Mongo/Collection.html)\n\n### GridFS\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\ndatabase = client[\"database_name\"]\n\n# A GridFS bucket belong to a database.\ngridfs = database.grid_fs\n\n# Upload\nfile = File.new(\"file.txt\")\nid = gridfs.upload_from_stream(\"file.txt\", file)\nfile.close\n\n# Download\nstream = IO::Memory.new\ngridfs.download_to_stream(id, stream)\nputs stream.rewind.gets_to_end\n\n# Find\nfiles = gridfs.find({\n  length: {\"$gte\": 5000},\n})\nfiles.each { |file|\n  puts file.filename\n}\n\n# Delete\ngridfs.delete(id)\n\n# And many more methods… (check the link below.)\n```\n\n**Links**\n\n- [Mongo::GridFS::Bucket](https://elbywan.github.io/cryomongo/Mongo/GridFS/Bucket.html)\n\n### Change streams\n\n```crystal\nrequire \"cryomongo\"\n\n# Change streams can watch a client, database or collection for change.\n# This code snippet will focus on watching a single collection.\n\nclient = Mongo::Client.new\ncollection = client[\"database_name\"][\"collection_name\"]\n\nspawn {\n  cursor = collection.watch(\n    [\n      {\"$match\": {\"operationType\": \"insert\"}},\n    ],\n    max_await_time_ms: 10000\n  )\n  # cursor.of(BSON) converts fetched elements to the Mongo::ChangeStream::Document(BSON) type.\n  cursor.of(BSON).each { |doc|\n    puts doc.document_key\n    puts doc.full_document.to_json\n  }\n}\n\n100.times do |i|\n  collection.insert_one({count: i})\nend\n\nsleep\n```\n\n**Links**\n\n- [Mongo::ChangeStream::Cursor](https://elbywan.github.io/cryomongo/Mongo/ChangeStream/Cursor.html)\n- [Mongo::ChangeStream::Document](https://elbywan.github.io/cryomongo/Mongo/ChangeStream/Document.html)\n\n## Raw commands\n\n```crystal\nrequire \"cryomongo\"\n\n# Commands can be run on a client, database or collection depending on the command target.\n\nclient = Mongo::Client.new\n\n# Call the `.command` method to run a command against the server.\n# The first argument is a `Mongo::Commands` sub-class, followed by the mandatory arguments\n# and finally an *options* named tuple containing the optional parameters in snake_case.\nresult = client.command(Mongo::Commands::ServerStatus, options: {\n  repl: 0\n})\nputs result.to_bson\n\n# The .command method can also be called against a Database…\nclient[\"database\"].command(Mongo::Commands::Create, name: \"collection\")\nclient[\"database\"].command(Mongo::Commands::Drop, name: \"collection\")\n# …or a Collection.\nclient[\"database\"][\"collection\"].command(Mongo::Commands::Validate)\n```\n**Links**\n\n- [Mongo::Commands](https://elbywan.github.io/cryomongo/Mongo/Commands.html)\n- [Mongo::Client#command](https://elbywan.github.io/cryomongo/Mongo/Client.html#command(command,write_concern:WriteConcern?=nil,read_concern:ReadConcern?=nil,read_preference:ReadPreference?=nil,server_description:SDAM::ServerDescription?=nil,session:Session::ClientSession?=nil,operation_id:Int64?=nil,**args)-instance-method)\n- [Mongo::Database#command](https://elbywan.github.io/cryomongo/Mongo/Database.html#command(operation,write_concern:WriteConcern?=nil,read_concern:ReadConcern?=nil,read_preference:ReadPreference?=nil,session:Session::ClientSession?=nil,**args)-instance-method)\n- [Mongo::Collection#command](https://elbywan.github.io/cryomongo/Mongo/Collection.html#command(operation,write_concern:WriteConcern?=nil,read_concern:ReadConcern?=nil,read_preference:ReadPreference?=nil,session:Session::ClientSession?=nil,**args)-instance-method)\n\n## Concerns and Preference\n\n```crystal\nrequire \"cryomongo\"\n\n# Instantiate Read/Write Concerns and Preference\nread_concern = Mongo::ReadConcern.new(level: \"majority\")\nwrite_concern = Mongo::WriteConcern.new(w: 1, j: true)\nread_preference = Mongo::ReadPreference.new(mode: \"primary\")\n\n# They can be set at the client, database or client level…\nclient = Mongo::Client.new\ndatabase = client[\"database_name\"]\ncollection = database[\"collection_name\"]\n\nclient.read_concern = read_concern\ndatabase.write_concern = write_concern\ncollection.read_preference = read_preference\n\n# …or by passing an extra argument when calling a method.\ncollection.find(\n  filter: { key: \"value\" },\n  read_concern:  Mongo::ReadConcern.new(level: \"local\"),\n  read_preference: Mongo::ReadPreference.new(mode: \"secondary\")\n)\n```\n\n**Links**\n\n- [Mongo::ReadConcern](https://elbywan.github.io/cryomongo/Mongo/ReadConcern.html)\n- [Mongo::WriteConcern](https://elbywan.github.io/cryomongo/Mongo/WriteConcern.html)\n- [Mongo::ReadPreference](https://elbywan.github.io/cryomongo/Mongo/ReadPreference.html)\n\n## Commands Monitoring\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\n\n# A simple logging subscriber.\n\nsubscription = client.subscribe_commands { |event|\n  case event\n  when Mongo::Monitoring::Commands::CommandStartedEvent\n    Log.info { \"COMMAND.#{event.command_name} #{event.address} STARTED: #{event.command.to_json}\" }\n  when Mongo::Monitoring::Commands::CommandSucceededEvent\n    Log.info { \"COMMAND.#{event.command_name} #{event.address} COMPLETED: #{event.reply.to_json} (#{event.duration}s)\" }\n  when Mongo::Monitoring::Commands::CommandFailedEvent\n    Log.info { \"COMMAND.#{event.command_name} #{event.address} FAILED: #{event.failure.inspect} (#{event.duration}s)\" }\n  end\n}\n\n# Make some queries…\nclient[\"database_name\"][\"collection_name\"].find({ hello: \"world\" })\n\n# …and eventually at some point, unsubscribe the logger.\nclient.unsubscribe_commands(subscription)\n```\n\n**Links**\n\n- [Mongo::Client#subscribe_commands](https://elbywan.github.io/cryomongo/Mongo/Client.html#subscribe_commands(\u0026callback:Monitoring::Commands::Event-\u003eNil):Monitoring::Commands::Event-\u003eNil-instance-method)\n- [Mongo::Client#unsubscribe_commands](https://elbywan.github.io/cryomongo/Mongo/Client.html#unsubscribe_commands(callback:Monitoring::Commands::Event-\u003eNil):Nil-instance-method)\n- [Mongo::Monitoring::Observable](https://elbywan.github.io/cryomongo/Mongo/Monitoring/Observable.html)\n- [Mongo::Monitoring::CommandStartedEvent](https://elbywan.github.io/cryomongo/Mongo/Monitoring/Commands/CommandStartedEvent.html)\n- [Mongo::Monitoring::CommandSucceededEvent](https://elbywan.github.io/cryomongo/Mongo/Monitoring/Commands/CommandSucceededEvent.html)\n- [Mongo::Monitoring::CommandFailedEvent](https://elbywan.github.io/cryomongo/Mongo/Monitoring/Commands/CommandFailedEvent.html)\n\n## Causal Consistency\n\n```crystal\nrequire \"cryomongo\"\n\nclient = Mongo::Client.new\n# It is important to ensure that both read and writes are performed with \"majority\" concern.\n# See: https://docs.mongodb.com/manual/core/causal-consistency-read-write-concerns/\nclient.read_concern = Mongo::ReadConcern.new(level: \"majority\")\nclient.write_concern = Mongo::WriteConcern.new(w: \"majority\")\n\n# Reusing the original Mongodb example.\n# See: https://docs.mongodb.com/manual/core/read-isolation-consistency-recency/#examples\n\ncurrent_date = Time.utc\nitems = client[\"test\"][\"items\"]\n\n# MongoDB enables causal consistency in client sessions by default.\n# This is the block syntax that creates, ends and pass the session to collection methods automatically.\nitems_collection.with_session do |items|\n  # Using a causally consistent session ensures that the update occurs before the insert.\n  items.update_one(\n    { sku: \"111\", end: { \"$exists\": false } },\n    { \"$set\": { end: current_date }}\n  )\n  items.insert_one({ sku: \"nuts-111\", name: \"Pecans\", start: current_date })\n  puts items.find.to_a.to_pretty_json\nend\n\nclient.close\n```\n\n**Links**\n\n- [Mongo::Session](https://elbywan.github.io/cryomongo/Mongo/Session.html)\n- [Mongo::Client#start_session](https://elbywan.github.io/cryomongo/Mongo/Client.html#start_session(*,causal_consistency:Bool=true):Session::ClientSession-instance-method)\n- [Mongo::Collection#with_session](https://elbywan.github.io/cryomongo/Mongo/Collection.html#with_session(**args,\u0026)-instance-method)\n\n## Transactions\n\n```crystal\nrequire \"cryomongo\"\n\n# Initialize Client and Database instances.\nclient = Mongo::Client.new\ndatabase = client[\"db\"]\ncollection = database[\"collection\"]\n\n# Create the collection.\n{Mongo::Commands::Drop, Mongo::Commands::Create}.each do |command|\n  database.command(command, name: \"collection\")\nrescue e : Mongo::Error::Command\n  # ignore the server error, drop will fail if the collection has not been created before.\nend\n\n# Set read and write concerns to perform isolated transactions.\n# See: https://docs.mongodb.com/master/core/transactions/#transactions-and-sessions\ntransaction_options = Mongo::Session::TransactionOptions.new(\n  read_concern: Mongo::ReadConcern.new(level: \"snapshot\"),\n  write_concern: Mongo::WriteConcern.new(w: \"majority\")\n)\n\n# There are two ways to perform transactions:\n\ncollection.with_session(default_transaction_options: transaction_options) do |collection, session|\n  puts collection.find.to_a.to_json # =\u003e \"[]\"\n\n  # 1. by calling the `with_transaction` method.\n\n  # `with_transaction` will commit after the block ends.\n  # if the block raises, the transaction will be aborted.\n  session.with_transaction {\n    collection.insert_one({_id: 1})\n    collection.insert_one({_id: 2})\n  }\n  puts collection.find.to_a.to_json # =\u003e [{\"_id\":1},{\"_id\":2}]\n\n  # The transaction below will be aborted because the block raises an Exception.\n  begin\n    session.with_transaction {\n      collection.insert_one({_id: 3})\n      raise \"Interrupted!\"\n      collection.insert_one({_id: 4})\n    }\n  rescue e\n    puts e # =\u003e Interrupted!\n  end\n  puts collection.find.to_a.to_json # =\u003e [{\"_id\":1},{\"_id\":2}]\n\n  # 2. by calling the `start_transaction`, `commit_transaction` and `abort_transaction` methods.\n  session.start_transaction\n  collection.insert_one({_id: 3})\n  # The transaction is isolated, reading outside of the session scope does not return documents impacted by the transaction…\n  puts database[\"collection\"].find.to_a.to_json # =\u003e [{\"_id\":1},{\"_id\":2}]\n  # but reading within the session scope does.\n  puts collection.find.to_a.to_json # =\u003e [{\"_id\":1},{\"_id\":2},{\"_id\":3}]\n  session.commit_transaction\n  # The transaction is now committed and visible outside of the transaction scope.\n  puts collection.find.to_a.to_json             # =\u003e [{\"_id\":1},{\"_id\":2},{\"_id\":3}]\n  puts database[\"collection\"].find.to_a.to_json # =\u003e [{\"_id\":1},{\"_id\":2},{\"_id\":3}]\nend\n```\n\n**Links**\n\n- [Mongo::Session#with_transaction](https://elbywan.github.io/cryomongo/Mongo/Session/ClientSession.html#with_transaction(**options,\u0026)-instance-method)\n- [Mongo::Session#start_transaction](https://elbywan.github.io/cryomongo/Mongo/Session/ClientSession.html#start_transaction(**options)-instance-method)\n- [Mongo::Session#commit_transaction](https://elbywan.github.io/cryomongo/Mongo/Session/ClientSession.html#commit_transaction(*,write_concern:WriteConcern?=nil)-instance-method)\n- [Mongo::Session#abort_transaction](https://elbywan.github.io/cryomongo/Mongo/Session/ClientSession.html#abort_transaction(*,write_concern:WriteConcern?=nil)-instance-method)\n- [Mongo::Session::TransactionOptions](https://elbywan.github.io/cryomongo/Mongo/Session/TransactionOptions.html)\n\n## Specifications\n\nThe goal is to to be compliant with most of the [official MongoDB set of specifications](https://github.com/mongodb/specifications).\n\n**✅ Implemented**\n\n The following specifications are implemented:\n\n- https://github.com/mongodb/specifications/tree/master/source/message\n- https://github.com/mongodb/specifications/tree/master/source/crud\n- https://github.com/mongodb/specifications/blob/master/source/find_getmore_killcursors_commands.rst\n- https://github.com/mongodb/specifications/blob/master/source/driver-bulk-update.rst\n- https://github.com/mongodb/specifications/blob/master/source/read-write-concern/read-write-concern.rst\n- https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst\n- https://github.com/mongodb/specifications/blob/master/source/enumerate-databases.rst\n- https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst\n- https://github.com/mongodb/specifications/tree/master/source/connection-string\n- https://github.com/mongodb/specifications/tree/master/source/uri-options (except validation)\n- https://github.com/mongodb/specifications/tree/master/source/server-discovery-and-monitoring\n- https://github.com/mongodb/specifications/blob/master/source/server-selection/server-selection.rst\n- https://github.com/mongodb/specifications/blob/master/source/max-staleness/max-staleness.rst\n- https://github.com/mongodb/specifications/tree/master/source/connection-monitoring-and-pooling (loosely - using the crystal-db pool)\n- https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst (SHA1 / SHA256 only - without SASLprep)\n- https://github.com/mongodb/specifications/blob/master/source/index-management.rst (no IndexView fluid syntax)\n- https://github.com/mongodb/specifications/tree/master/source/gridfs\n- https://github.com/mongodb/specifications/tree/master/source/change-streams\n- https://github.com/mongodb/specifications/blob/master/source/sessions/driver-sessions.rst\n- https://github.com/mongodb/specifications/tree/master/source/command-monitoring\n- https://github.com/mongodb/specifications/blob/master/source/retryable-writes/retryable-writes.rst\n- https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst\n- https://github.com/mongodb/specifications/tree/master/source/causal-consistency\n- https://github.com/mongodb/specifications/blob/master/source/initial-dns-seedlist-discovery\n- https://github.com/mongodb/specifications/tree/master/source/transactions\n\n**⏳Next**\n\nThe following specifications are to be implemented next:\n\n- https://github.com/mongodb/specifications/blob/master/source/polling-srv-records-for-mongos-discovery\n- https://github.com/mongodb/specifications/tree/master/source/compression\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/elbywan/cryomongo/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [elbywan](https://github.com/elbywan) - creator and maintainer\n\n## Credit\n\n- Icon made by [Smashicons](https://www.flaticon.com/authors/smashicons) from [www.flaticon.com](https://www.flaticon.com).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felbywan%2Fcryomongo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felbywan%2Fcryomongo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felbywan%2Fcryomongo/lists"}