Logo

0x3d.Site

is designed for aggregating information.
Welcome
check repository here

couchdb_connector

Please note: This project is now in maintenance mode. I will continue to take bug reports and act on them in as timely a manner as I can. If additional features are desired, please feel free to submit feature requests in the form of issues. I intend to cut one further release shortly that will likely contain work that addresses the currently open 2 issues and 1 PR.

Build Status Coverage Status Hex.pm API Docs Hex.pm

Description

A connector for CouchDB, the Erlang-based, JSON document database.

The connector does not implement the protocols defined in Ecto. Reasons: CouchDB does not support transactions as known in the world of ACID compliant, relational databases. The concept of migrations also does not apply to CouchDB. And since CouchDB does not implement an SQL dialect, the decision was taken to not follow the standards established by Ecto.

The connector offers create, update and read operations through its Writer and Reader modules. Basic support for view operations is provided by the View module.

All create and update operations expect valid JSON documents. All read operations return JSON strings exactly as they come from CouchDB.

In addition to this representation, Release 0.4 introduced a second format for documents. Users can now also retrieve and ingest documents represented as nested Maps.

The connector also offers functions to manage users and admins. These functions have been implemented to support testing of authentication and most users will probably manage users through different tools.

HTTP Basic access authentication (Basic auth) is currently the only supported authentication scheme.

Supported platforms

The current release of the connector has been tested successfully with Elixir release versions 1.2.6, 1.3.4 and 1.4.0, using Erlang OTP in versions 18.2.1 and 19.2 as well as CouchDB version 1.6.1.

Installation

The module is available in Hex, the package can be installed as follows:

  1. Add couchdb_connector to your list of dependencies in mix.exs:
def deps do
  [{:couchdb_connector, "~> 0.5.0"}]
end
  1. Ensure couchdb_connector is started before your application:
def application do
  [applications: [:couchdb_connector]]
end

Usage

For the subsequent steps, let's assume that we work in iex and that we define these database properties:

db_props = %{protocol: "http", hostname: "localhost", database: "couchdb_connector_dev", port: 5984}

Authentication

HTTP Basic authentication was first introduced in release version 0.3. Some versions and improvements later, the current best practice is to put the Basic auth credentials into the database properties like so:

db_props = %{protocol: "http", hostname: "localhost", database: "couchdb_connector_dev", port: 5984,
user: "username", password: "secret"}

Support for this configuration feature has been introduced in release version 0.4.4. It relieves users from having to add the credentials to each authenticated function call. Authentication can now be dealt with once and in one place only.

Create a database

Couchdb.Connector.Storage.storage_up(db_props)

You should see

{:ok, "{\"ok\":true}\n"}

In case the database already exists of course, you would see

{:error,
  "{\"error\":\"file_exists\",\"reason\":\"The database could not be created, the file already exists.\"}\n"}

Write to a database

Now that the database exits, we can create documents in it.

Couchdb.Connector.Writer.create_generate(db_props, "{\"key\": \"value\"}")

You should see something similar to this

{:ok,
  "{\"ok\":true,\"id\":\"6b66e9bd59c1c35f3ab51165b10889a7\",\"rev\":\"1-59414e77c768bc202142ac82c2f129de\"}\n",
    [{"Server", "CouchDB/1.6.1 (Erlang OTP/18)"},
    {"Location",
    "http://localhost:5984/couchdb_connector_dev/6b66e9bd59c1c35f3ab51165b10889a7"},
    {"ETag", "\"1-59414e77c768bc202142ac82c2f129de\""},
    {"Date", "Sun, 27 Dec 2015 22:28:15 GMT"},
    {"Content-Type", "text/plain; charset=utf-8"}, {"Content-Length", "95"},
    {"Cache-Control", "must-revalidate"}]}

In the previous example, we had CouchDB assign a generated id to the document. This will do for most cases. In case you want to provide an id and you are sure that it does not yet exist in the database, you can do this:

Couchdb.Connector.Writer.create(db_props, "{\"key\": \"value\"}", "unique_id")

You should then see something like

{:ok,
  "{\"ok\":true,\"id\":\"unique_id\",\"rev\":\"1-59414e77c768bc202142ac82c2f129de\"}\n",
    [{"Server", "CouchDB/1.6.1 (Erlang OTP/18)"},
    {"Location", "http://localhost:5984/couchdb_connector_dev/unique_id"},
    {"ETag", "\"1-59414e77c768bc202142ac82c2f129de\""},
    {"Date", "Sun, 27 Dec 2015 22:32:45 GMT"},
    {"Content-Type", "text/plain; charset=utf-8"}, {"Content-Length", "72"},
    {"Cache-Control", "must-revalidate"}]}

Write to a database — Input given as Map

Starting with version 0.4, you can now also pass in the document as a Map instead of using the JSON String representation. To do so, make use of the top-level API given in the module Couchdb.Connector:

Couchdb.Connector.create(TestConfig.database_properties, %{"key" => "value"}, "42")

The response should look similar to this:

{:ok,
 %{headers: %{"Cache-Control" => "must-revalidate", "Content-Length" => "65",
     "Content-Type" => "text/plain; charset=utf-8",
     "Date" => "Thu, 10 Nov 2016 22:22:18 GMT",
     "ETag" => "\"1-59414e77c768bc202142ac82c2f129de\"",
     "Location" => "http://127.0.0.1:5984/couchdb_connector_dev/42",
     "Server" => "CouchDB/1.6.1 (Erlang OTP/19)"},
   payload: %{"id" => "42", "ok" => true,
     "rev" => "1-59414e77c768bc202142ac82c2f129de"}}}

In other words, the connector wraps headers and payload in nested Maps — Cool! Note that the handling is uniform regardless of whether an operation succeeds or fails. An error response will look the same as a success response does, with the exception of the :error atom replacing the :ok atom.

{:error,
  %{headers: => %{...},
    payload: %{"error" => "...", ...}}}

Read from a database

Given we have a document under the id "unique_id" in the database that we created in one of the steps above, the following "GET" should return the desired document.

So let's try

Couchdb.Connector.Reader.get(db_props, "unique_id")

You should see something akin to this:

{:ok,
  "{\"_id\":\"unique_id\",\"_rev\":\"1-59414e77c768bc202142ac82c2f129de\",\"key\":\"value\"}\n"}

In case you ask for a non existing document, like in this example

Couchdb.Connector.Reader.get(db_props, "wrong_id")

You should see something this:

{:error,  "{\"error\":\"not_found\",\"reason\":\"missing\"}\n"}

Read from a database — Response wrapped in a Map

Also starting with version 0.4, you can now retrieve a CouchDB document given as a Map instead of the JSON String representation. To do so, make use of the top-level API given in the module Couchdb.Connector:

Couchdb.Connector.get(TestConfig.database_properties, "42")

The response should look similar to this:

{:ok,
 %{"_id" => "42", "_rev" => "1-59414e77c768bc202142ac82c2f129de",
   "key" => "value"}}

The error case (document does not exist) looks like this:

{:error, %{"error" => "not_found", "reason" => "missing"}}

Update a document

CouchDB demands that clients pass in the document's current revision to make sure that the update operation occurs on the current version of the document. An update request therefore looks like this:

Couchdb.Connector.Writer.update(db_props, "{\"key\": \"new value\"}", "0a89648ca060...", "1-7b2f4edf07d0...")

and the response should be similar to this:

{:ok,
 "{\"ok\":true,\"id\":\"0a89648ca060...\",\"rev\":\"2-7b2f4edf07d0...\"}\n",
 [{"Server", "CouchDB/1.6.1 (Erlang OTP/19)"},
  {"Location",
   "http://127.0.0.1:5984/couchdb_connector_test/0a89648ca060..."},
  {"ETag", "\"2-7b2f4edf07d0...\""},
  {"Date", "Sat, 03 Dec 2016 13:57:07 GMT"},
  {"Content-Type", "text/plain; charset=utf-8"}, {"Content-Length", "95"},
  {"Cache-Control", "must-revalidate"}]}

Note that an update increments the revision. In the example above, the new revision now starts with 2, indicating that one update has happened since the document was first created. Also note that the response does not contain the updated document but only states that the update succeeded.

Update a document — Response wrapped in a Map

Version 0.4.2 introduced a Map based version of the update API. Let's say we have a document bound to a variable called current. Interacting with the API would then look as follows:

updated = %{current | "key" => "new value"}
{:ok, %{:headers => h, :payload => p}} = Connector.update(db_props, updated)

The response would look similar to this:

{:ok,
 %{headers: %{"Cache-Control" => "must-revalidate", "Content-Length" => "95",
     "Content-Type" => "text/plain; charset=utf-8",
     "Date" => "Sat, 03 Dec 2016 14:21:10 GMT",
     "ETag" => "\"2-7b2f4edf07...\"",
     "Location" => "http://127.0.0.1:5984/couchdb_connector_test/8b7b622e37c5c8fa9d6505ecb800197f",
     "Server" => "CouchDB/1.6.1 (Erlang OTP/19)"},
   payload: %{"id" => "8b7b622e37c5...", "ok" => true,
     "rev" => "2-7b2f4edf07..."}}}

Delete a document

In order to delete a document, you have to pass in its current revision, the same way that you saw above for the update calls.

So in response to a call like this:

Couchdb.Connector.Writer.destroy(db_props, "42", "1-9b2e3bcc3752...")

You should see a response like this:

"{\"ok\":true,\"id\":\"42\",\"rev\":\"2-9b2e3bcc3752a3...\"}\n"

Delete a document — Response wrapped in a Map

Since release 0.4.2, there is also an implementation of the delete functionality in place that wraps the resopnse in a Map. It is located in the top level module Couchdb.Connector and its API is identical to the JSON/String version:

Couchdb.Connector.destroy(db_props, "42", "1-9b2e3bcc3752...")

gives

%{"id" => "42", "ok" => true, "rev" => "2-9b2e3bcc3752a3..."}

Create a View

CouchDB Views are defined in JavaScript and consist of mappers and (optional) reducers. Views are grouped together in CouchDB in what is known as Design Documents.

Let's assume that you want to create one or more Views as part of a seeding process. In order to do so, you can encode your Views in JSON files as follows:

{
  "_id" : "_design/example",
  "views" : {
    "by_name" : {
      "map" : "function(doc){ emit(doc.name, doc)}"
    }
  }
}

Creating this View can then be done through the connector like this:

{:ok, code} = File.read("path/to/view.json")
{:ok, _} = Couchdb.Connector.View.create_view(db_props, "example", code)

where "example" is the name of the design document and code now contains the JavaScript as read from file.

You should see something like

{:ok,
 "{\"ok\":true,\"id\":\"_design/example\",\"rev\":\"1-175ebbcc6e519413aeb640e8fc63424d\"}\n"}

Query a View

Querying a View can be done like this:

{:ok, result} = Couchdb.Connector.View.document_by_key(db_props, "design_name", "view_name", "key")

In case the document given by "key" exists, you should see something like

{:ok,
 "{\"total_rows\":3,\"offset\":1,\"rows\":[\r\n{\"id\":\"5c09dbf93fd6226c...\",\"key\":\"key\",..."}

otherwise, the response should contain an empty list of rows:

{:ok, "{\"total_rows\":0,\"offset\":0,\"rows\":[\r\n\r\n]}\n"}

Destroy a database

Couchdb.Connector.Storage.storage_down(db_props)

You should see

{:ok, "{\"ok\":true}\n"}

In case that database never existed, you should see

{:error, "{\"error\":\"not_found\",\"reason\":\"missing\"}\n"}

Next

Love to hear from you. Meanwhile, here are some things I'd like to tackle next:

  • pool connections to CouchDB
  • enhance view handling and query capabilities
  • cookie auth, oauth auth
  • attachment support
  • improve documentation
  • complete function specs
Elixir
Elixir
Elixir is a dynamic, functional programming language designed for building scalable and maintainable applications. Built on the Erlang VM, it's known for its high concurrency and fault tolerance, making it ideal for real-time systems and web services.
GitHub - chrismccord/atlas: Object Relational Mapper for Elixir
GitHub - chrismccord/atlas: Object Relational Mapper for Elixir
GitHub - mbuhot/ecto_job: Transactional job queue with Ecto, PostgreSQL and GenStage
GitHub - mbuhot/ecto_job: Transactional job queue with Ecto, PostgreSQL and GenStage
GitHub - zamith/tomlex: A TOML parser for elixir
GitHub - zamith/tomlex: A TOML parser for elixir
GitHub - pablomartinezalvarez/glayu: A static site generator for mid-sized sites.
GitHub - pablomartinezalvarez/glayu: A static site generator for mid-sized sites.
GitHub - jui/mustachex: Mustache for Elixir
GitHub - jui/mustachex: Mustache for Elixir
GitHub - joaothallis/elixir-auto-test: Run test when file is saved
GitHub - joaothallis/elixir-auto-test: Run test when file is saved
GitHub - campezzi/ignorant: Simplify comparison of Elixir data structures by ensuring fields are present but ignoring their values.
GitHub - campezzi/ignorant: Simplify comparison of Elixir data structures by ensuring fields are present but ignoring their values.
GitHub - Driftrock/mockingbird: A set of helpers to create http-aware modules that are easy to test.
GitHub - Driftrock/mockingbird: A set of helpers to create http-aware modules that are easy to test.
GitHub - gutschilla/elixir-pdf-generator: Create PDFs with wkhtmltopdf or puppeteer/chromium from Elixir.
GitHub - gutschilla/elixir-pdf-generator: Create PDFs with wkhtmltopdf or puppeteer/chromium from Elixir.
GitHub - antirez/disque: Disque is a distributed message broker
GitHub - antirez/disque: Disque is a distributed message broker
GitHub - jcomellas/ex_hl7: HL7 Parser for Elixir
GitHub - jcomellas/ex_hl7: HL7 Parser for Elixir
GitHub - Cirru/parser.ex: Cirru Parser in Elixir
GitHub - Cirru/parser.ex: Cirru Parser in Elixir
GitHub - thiamsantos/pwned: Check if your password has been pwned
GitHub - thiamsantos/pwned: Check if your password has been pwned
GitHub - suvash/hulaaki: DEPRECATED : An Elixir library (driver) for clients communicating with MQTT brokers(via the MQTT 3.1.1 protocol).
GitHub - suvash/hulaaki: DEPRECATED : An Elixir library (driver) for clients communicating with MQTT brokers(via the MQTT 3.1.1 protocol).
GitHub - sinetris/factory_girl_elixir: Minimal implementation of Ruby's factory_girl in Elixir.
GitHub - sinetris/factory_girl_elixir: Minimal implementation of Ruby's factory_girl in Elixir.
GitHub - navinpeiris/ex_unit_notifier: Desktop notifications for ExUnit
GitHub - navinpeiris/ex_unit_notifier: Desktop notifications for ExUnit
GitHub - DefactoSoftware/test_selector: Elixir library to help selecting the right elements in your tests.
GitHub - DefactoSoftware/test_selector: Elixir library to help selecting the right elements in your tests.
GitHub - xerions/ecto_migrate: Automatic migrations for ecto
GitHub - xerions/ecto_migrate: Automatic migrations for ecto
GitHub - meh/reagent: You need more reagents to conjure this server.
GitHub - meh/reagent: You need more reagents to conjure this server.
GitHub - stevegraham/hypermock: HTTP request stubbing and expectation Elixir library
GitHub - stevegraham/hypermock: HTTP request stubbing and expectation Elixir library
GitHub - msharp/elixir-statistics: Statistical functions and distributions for Elixir
GitHub - msharp/elixir-statistics: Statistical functions and distributions for Elixir
GitHub - Joe-noh/colorful: colorful is justice
GitHub - Joe-noh/colorful: colorful is justice
GitHub - ijcd/taggart: HTML as code in Elixir
GitHub - ijcd/taggart: HTML as code in Elixir
Build software better, together
Build software better, together
GitHub - yeshan333/ex_integration_coveralls: A library for run-time system code line-level coverage analysis.
GitHub - yeshan333/ex_integration_coveralls: A library for run-time system code line-level coverage analysis.
GitHub - PSPDFKit-labs/cobertura_cover: Output test coverage information in Cobertura-compatible format
GitHub - PSPDFKit-labs/cobertura_cover: Output test coverage information in Cobertura-compatible format
GitHub - basho/enm: Erlang driver for nanomsg
GitHub - basho/enm: Erlang driver for nanomsg
GitHub - pawurb/ecto_psql_extras: Ecto PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
GitHub - pawurb/ecto_psql_extras: Ecto PostgreSQL database performance insights. Locks, index usage, buffer cache hit ratios, vacuum stats and more.
GitHub - crate/craterl: Client Libraries for Erlang
GitHub - crate/craterl: Client Libraries for Erlang
GitHub - sheharyarn/ecto_rut: Ecto Model shortcuts to make your life easier! :tada:
GitHub - sheharyarn/ecto_rut: Ecto Model shortcuts to make your life easier! :tada:
Elixir
More on Elixir

Programming Tips & Tricks

Code smarter, not harder—insider tips and tricks for developers.

Error Solutions

Turn frustration into progress—fix errors faster than ever.

Shortcuts

The art of speed—shortcuts to supercharge your workflow.
  1. Collections 😎
  2. Frequently Asked Question's 🤯

Tools

available to use.

Made with ❤️

to provide resources in various ares.