CosmosDbEx (cosmos_db_ex v0.1.0) View Source

Contains the basic functions needed to communicate with CosmosDb.

Configuration

There are specific configuration values that must be present in order to communicate with your instance of Cosmos Db. They are:

  • COSMOS DB KEY - This is either the primary or secondary Read-Write key of your database. You can find these keys under the 'Keys' tab of your databases settings. This value can be set in your apps config using :cosmos_db_key or as an environment variable named COSMOS_DB_KEY.

  • COSMOS DB HOST URL - This is the URI to your Cosmos DB instance. The value can be found under the Essentials section of your Cosmos Db Overview. This value can be set in your apps config using :cosmos_db_host_url or as an environment variable named COSMOS_DB_HOST_URL.

Example using your apps configuration settings:

config :cosmos_db_ex,
  cosmos_db_key: "{your_primary_or_secondary_key_here}",
  cosmos_db_host_url: "https://your-cosmos-db.documents.azure.com/",

Remember that your keys are secrets that should never be saved in any version control system. If you're going to use the config option please take caution and look at using a config provider that retrieves the secrets from a vault or the environment.

Link to this section Summary

Functions

Creates a new document in the specified database and container.

Retrieve a document by it's document id and partition key.

Retrieve all documents in the container. This function defaults to retrieving 100 documents at a time (this is Cosmos Db's default). You can increase the amount of items returned by calling get_documents(container, max_item_count) with any number up to 1000 (Cosmos Dbs max).

Sends a query to Cosmos Db.

Link to this section Functions

Link to this function

create_document(container, document, partition_key)

View Source

Specs

create_document(CosmosDbEx.Container.t(), map(), String.t()) ::
  {:ok
   | :bad_request
   | :conflict
   | :entity_too_large
   | :not_found
   | :storage_limit_reached
   | :unauthorized, CosmosDbEx.Response.t()}

Creates a new document in the specified database and container.

Documents can be any struct that implements the CosmosDbEx.Client.Documents.Identifiable protocol, or any struct or map that contains an id field. The Identifiable protocol contains a single function that must be implemented, called get_id(). You can return a string in any format to represent the id given to CosmosDb.

NOTE: You must implement the Jason.Encoder protocol for any struct that will be used as the document being created. You can do this by adding the following to your struct definition:

@dervie {Jason.Encoder, only: [....]}
defstruct ...

Every request will return a tuple containing the status of the request as well as any information Cosmos Db returned in the body of the response. The only exception is when our call to the Rest API fails for a non-CosmosDb related issue

Here are the tuple pairs returned for the following situations:

  • {:ok, %CosmosDbEx.Response{}} - The operation was successful.
  • {:bad_request, %CosmosDbEx.Response{}} - The JSON body is invalid.
  • {:storage_limit_reached, %CosmosDbEx.Response{}} - The operation could not be completed because the storage limit of the partition has been reached.
  • {:conflict, %CosmosDbEx.Response{}} - The ID provided for the new document has been taken by an existing document.
  • {:entity_too_large, %CosmosDbEx.Response{}} - The document size in the request exceeded the allowable document size.
  • {:error, error} - Any errors encountered by our http client that aren't related to CosmosDb.

Examples

  iex> item = %{ name: "ACME hair dryer", id: "ACME-HD-WOLF01234", location: "Bottom of a cliff"}
  iex> container = CosmosDbEx.Container.new("database", "container")
  iex> container |> CosmosDbEx.create_document(item, item.name)
  {:ok,
   %CosmosDbEx.Response{
     body: %{
       "_attachments" => "attachments/",
       "_etag" => "00000000-0000-0000-0000-000000000000",
       "_rid" => "AAAAAAAAAAAAAAAAA==",
       "_self" => "dbs/AAAAAA==/colls/AAAAAAAAAAA=/docs/AAAAAAAAAAAAAAAAAAAAAA==/",
       "_ts" => 1620141668,
       "id" => "ACME-HD-WOLF01234",
       "location" => "Bottom of a cliff",
       "name" => "ACME hair dryer"
     },
     properties: %{
      request_charge: "6.29",
      request_duration: "5.328"
     }
    }
  }
Link to this function

get_document(container, id, partition_key)

View Source

Specs

get_document(CosmosDbEx.Container.t(), String.t(), String.t()) ::
  {:ok
   | :bad_request
   | :conflict
   | :entity_too_large
   | :not_found
   | :storage_limit_reached
   | :unauthorized, CosmosDbEx.Response.t()}

Retrieve a document by it's document id and partition key.

Examples

iex> container = CosmosDbEx.Container.new("database", "container")
iex> item_id = "00000000-0000-0000-0000-000000000000"
iex> partition_key = item_id
iex> CosmosDbEx.get_document(container, item_id, partition_key)
{:ok,
 %CosmosDbEx.Response{
  body: %{
    "_attachments" => "attachments/",
    "_etag" => ""00000000-0000-0000-0000-000000000000"",
    "_rid" => "AAAAAAAAAAAAAAAAA==",
    "_self" => "dbs/AAAAAA==/colls/AAAAAAAAAAA=/docs/AAAAAAAAAAAAAAAAAAAAAA==/",
    "_ts" => 1620141668,
    "id" => "00000000-0000-0000-0000-000000000000",
    "name" => "Test item"
  },
  properties: %{
    request_charge: "1",
    request_duration: "0.585"
  }
 }
}
Link to this function

get_documents(container, continuation_token)

View Source

Specs

get_documents(CosmosDbEx.Container.t(), map()) ::
  {:ok
   | :bad_request
   | :conflict
   | :entity_too_large
   | :not_found
   | :storage_limit_reached
   | :unauthorized, CosmosDbEx.Response.t()}

Retrieve all documents in the container. This function defaults to retrieving 100 documents at a time (this is Cosmos Db's default). You can increase the amount of items returned by calling get_documents(container, max_item_count) with any number up to 1000 (Cosmos Dbs max).

If Cosmos Db has more documents than the query returned there will be a Continuation Token present in the properties (has the key continuation_token). You continue retrieving all the documents by using the Continuation Token in your next call to get_documents. See the second example

Examples

iex> CosmosDbEx.Container.new("database", "container") |> CosmosDbEx.get_documents()
{:ok,
 %CosmosDbEx.Response{
  body: %{
    "Documents" => [
      %{
        "_attachments" => "attachments/",
        "_etag" => ""0200c45b-0000-0200-0000-609166640000"",
        "_rid" => "Hj8rAI2HN48BAAAAAAAAAA==",
        "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48BAAAAAAAAAA==/",
        "_ts" => 1620141668,
        "id" => "d22d663e-6fa1-49af-98f8-df397f266999",
        "name" => "Test item here"
      },
      %{
        "_attachments" => "attachments/",
        "_etag" => ""02003980-0000-0200-0000-60916f6d0000"",
        "_rid" => "Hj8rAI2HN48CAAAAAAAAAA==",
        "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48CAAAAAAAAAA==/",
        "_ts" => 1620143981,
        "id" => "bef3c1f3-3f66-49a3-ba77-6e8d0e641664",
        "name" => "This is a test"
      }
    ]
  },
  count: 2,
  properties: %{
    continuation_token: nil,
    request_charge: "1",
    request_duration: "0.66"
  },
  resource_id: "Hj8rAI2HN48="
 }
}

iex> container = CosmosDbEx.Container.new("database", "container")
iex> {:ok, response} = container |> CosmosDbEx.get_documents(2)
{:ok,
  %CosmosDbEx.Response{
    body: %{
      "Documents" => [
        %{
          "_attachments" => "attachments/",
          "_etag" => ""0200c45b-0000-0200-0000-609166640000"",
          "_rid" => "Hj8rAI2HN48BAAAAAAAAAA==",
          "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48BAAAAAAAAAA==/",
          "_ts" => 1620141668,
          "id" => "d22d663e-6fa1-49af-98f8-df397f266999",
          "name" => "Test item here"
        },
        %{
          "_attachments" => "attachments/",
          "_etag" => ""02003980-0000-0200-0000-60916f6d0000"",
          "_rid" => "Hj8rAI2HN48CAAAAAAAAAA==",
          "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48CAAAAAAAAAA==/",
          "_ts" => 1620143981,
          "id" => "bef3c1f3-3f66-49a3-ba77-6e8d0e641664",
          "name" => "This is a test"
        }
      ]
    },
    count: 2,
    properties: %{
      continuation_token: %{
        "range" => %{"max" => "FF", "min" => ""},
        "token" => "Hj8rAI2HN48CAAAAAAAAAA=="
      },
      request_charge: "1",
      request_duration: "0.429"
    },
    resource_id: "Hj8rAI2HN48="
   }
  }
  iex> {:ok, response} = container |> CosmosDbEx.get_documents(response.properties.continuation_token)
  {:ok,
    %CosmosDbEx.Response{
      body: %{
        "Documents" => [
          %{
            "_attachments" => "attachments/",
            "_etag" => ""82035043-0000-0200-0000-60a1bac30000"",
            "_rid" => "Hj8rAI2HN48DAAAAAAAAAA==",
            "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48DAAAAAAAAAA==/",
            "_ts" => 1621211843,
            "id" => "2323490-23-23-3923493293",
            "name" => "This is a test of the protocol"
          },
          %{
            "_attachments" => "attachments/",
            "_etag" => ""8203015f-0000-0200-0000-60a1c47e0000"",
            "_rid" => "Hj8rAI2HN48FAAAAAAAAAA==",
            "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48FAAAAAAAAAA==/",
            "_ts" => 1621214334,
            "id" => "ACME-HD-WOLF01234",
            "location" => "Bottom of a cliff",
            "name" => "ACME hair dryer"
          },
          %{
            "_attachments" => "attachments/",
            "_etag" => ""5d00e34c-0000-0200-0000-60b41c140000"",
            "_rid" => "Hj8rAI2HN48GAAAAAAAAAA==",
            "_self" => "dbs/Hj8rAA==/colls/Hj8rAI2HN48=/docs/Hj8rAI2HN48GAAAAAAAAAA==/",
            "_ts" => 1622416404,
            "id" => "TestDoc-2-1-3-2",
            "location" => "Under da hill",
            "name" => "Just another test document"
          }
        ]
      },
      count: 3,
      properties: %{
        continuation_token: nil,
        request_charge: "1",
        request_duration: "0.596"
      },
      resource_id: "Hj8rAI2HN48="
    }
  }
Link to this function

get_documents(container, max_item_count \\ 100, continuation_token \\ nil)

View Source

Specs

get_documents(CosmosDbEx.Container.t(), integer(), map() | nil) ::
  {:ok
   | :bad_request
   | :conflict
   | :entity_too_large
   | :not_found
   | :storage_limit_reached
   | :unauthorized, CosmosDbEx.Response.t()}
Link to this function

query(container, query_text, params)

View Source

Specs

query(CosmosDbEx.Container.t(), String.t(), list()) ::
  {:ok
   | :bad_request
   | :conflict
   | :entity_too_large
   | :not_found
   | :storage_limit_reached
   | :unauthorized, CosmosDbEx.Response.t()}

Sends a query to Cosmos Db.

Parameters

  • query: The query contains the SQL query text.
  • params: A List of key/value pairs that correspond to values in the query.

Query String and Params

Notice the query string in the example below. For each value that will be substituted by an entry in the params list, the key in the query string must be annotated with an '@' symbol.

Example:

iex> query_text = "SELECT * FROM ItemsContainer c WHERE c.id = @id and c.name = @name"
iex> params = [{"id", "ACME-HD-WOLF01234"}, {"name", "ACME hair dryer"}]
iex> CosmosDbEx.query(query_text, params)
{:ok,
 %CosmosDbEx.Response{
   body: %{
     "Documents" => [
       %{
         "_attachments" => "attachments/",
         "_etag" => ""8203015f-0000-0200-0000-60a1c47e0000"",
         "_rid" => "AAarArAAAAAFAAAAAAAAAA==",
         "_ts" => 1621214334,
         "id" => "ACME-HD-WOLF01234",
         "location" => "Bottom of a cliff",
         "name" => "ACME hair dryer"
       }
     ]
   },
   count: 1,
   properties: %{
     continuation_token: nil,
     request_charge: "2.83",
     request_duration: "0.734"
   },
   resource_id: "AA8rAA2AN48="
 }
}