datomic_gen_server v1.0.1 DatomicGenServer.Db

DatomicGenServer.Db is a module intended to facilitate the use of Elixir data structures instead of edn strings for communicating with Datomic. This module maps the DatomicGenServer interface functions in wrappers that accept and return Elixir data structures, and also provides slightly more syntactically pleasant equivalents for Datomic keys and structures that would otherwise need to be represented using a lot of punctuation that isn’t required in Clojure.

The hexdoc organizes the functions in this module alphabetically; here is a list by topic:

Interface functions

q(server_identifier, exdn, options \ [])
transact(server_identifier, exdn, options \ [])
entity(server_identifier, exdn, attr_names \ :all, options \ [])

Datomic Shortcuts

Id/ident

dbid(db_part)
id
ident

Transaction creation

add
retract
install_attribute
alter_attribute
tx_instant

Value types

value_type
type_long
type_keyword
type_string
type_boolean
type_bigint
type_float
type_double
type_bigdec
type_ref
type_instant
type_uuid
type_uri
type_bytes

Cardinalities

cardinality
cardinality_one
cardinality_many

Optional Schema Attributes

doc
unique
unique_value
unique_identity
index
fulltext
is_component
no_history

Functions

_fn
fn_retract_entity
fn_cas

Common partions

schema_partition
transaction_partition
user_partition

Query placeholders

q?(atom)

Data sources

implicit
inS(placeholder_atom)

Bindings and find specifications

single_scalar
blank_binding
collection_binding(placeholder_atom)

Clauses

_not(inner_clause)
_not_join(binding_list, inner_clause_list)
_or(inner_clauses)
_or_join(binding_list, inner_clause_list)
_and(inner_clauses)
_pull({:symbol, entity_var}, pattern_clauses)
_expr(function_symbol, remaining_expressions, bindings)

Examples

DatomicGenServer.start(
  "datomic:mem://test", 
  true, 
  [{:timeout, 20_000}, {:default_message_timeout, 20_000}, {:name, DatomicGenServer}]
)

data_to_add = [%{ 
    Db.id => Db.dbid(Db.schema_partition),
    Db.ident => :"person/name",
    Db.value_type => Db.type_string,
    Db.cardinality => Db.cardinality_one,
    Db.doc => "A person's name",
    Db.install_attribute => Db.schema_partition
}]
Db.transact(DatomicGenServer, data_to_add)

# => {:ok, %DatomicGenServer.Db.DatomicTransaction{
        basis_t_before: 1001,   
        basis_t_after: 1002, 
        retracted_datoms: [],
        added_datoms: [
          %DatomicGenServer.Db.Datom{a: 50, added: true, e: 13194139534314, tx: 13194139534314, 
              v: %Calendar.DateTime{abbr: "UTC", day: 15, hour: 3, min: 20, month: 2, sec: 1, std_off: 0, 
                                    timezone: "Etc/UTC", usec: 746000, utc_off: 0, year: 2016}},
          %DatomicGenServer.Db.Datom{a: 41, added: true, e: 65, tx: 13194139534314, v: 35},
          %DatomicGenServer.Db.Datom{a: 62, added: true, e: 65, tx: 13194139534314, v: "A person's name"},
          %DatomicGenServer.Db.Datom{a: 10, added: true, e: 65, tx: 13194139534314, v: :"person/name"},
          %DatomicGenServer.Db.Datom{a: 40, added: true, e: 65, tx: 13194139534314, v: 23},
          %DatomicGenServer.Db.Datom{a: 13, added: true, e: 0, tx: 13194139534314, v: 65}],
        tempids: %{-9223367638809264706 => 65}}}

query = [:find, Db.q?(:c), :where, [Db.q?(:c), Db.doc, "A person's name"]]
Db.q(DatomicGenServer, query)

#=> {:ok, #MapSet<['A']>}  # ASCII representation of ID 65

Summary

Functions

Convenience shortcut for creating an and clause

Convenience shortcut for creating a Datomic expression clause

Convenience shortcut for :"db/fn"

Convenience shortcut for creating a not clause

Convenience shortcut for creating a not-join clause

Convenience shortcut for creating an or clause

Convenience shortcut for creating an or-join clause

Convenience shortcut for creating a Datomic pull expression

Convenience shortcut for :"db/add"

Convenience shortcut for :"db.alter/attribute"

Convenience shortcut for the blank binding _ as used, for example, in: [:find ?x :where [_ :likes ?x]]

Convenience shortcut for :"db/cardinality"

Convenience shortcut for :"db.cardinality/many"

Convenience shortcut for :"db.cardinality/one"

Convenience shortcut for collection binding find specification ... as used, for example, in: [:find ?e in $ [?a ...] :where [?e age ?a] ]

Convenience function that generates #db/id[ <partition> ]

Convenience shortcut for :"db/doc"

Issues an entity call to that is passed to the Datomic entity API function

Convenience shortcut for :"db.fn/cas"

Convenience shortcut for :"db.fn/retractEntity"

Convenience shortcut for :"db/fulltext"

Convenience shortcut for :"db/id"

Convenience shortcut for :"db/ident"

Convenience shortcut for the implicit data source $

Convenience function to generate Datomic data source specifications—i.e., symbols prefixed by a dollar sign

Convenience shortcut for :"db/index"

Convenience shortcut for :"db.install/_attribute"

Convenience shortcut for :"db/isComponent"

Convenience shortcut for :"db/noHistory"

Queries a DatomicGenServer using a query formulated as an Elixir list. This query is translated to an edn string which is then passed to the Datomic q API function

Convenience function to generate Datomic query placeholders—i.e., symbols prefixed by a question mark

Convenience shortcut for :"db/retract"

Convenience shortcut for :"db.part/db"

Convenience shortcut for the single scalar find specification . as used, for example, in: [:find ?e . :where [?e age 42] ]

Issues a transaction against a DatomicGenServer using a transaction formulated as an Elixir list of maps. This transaction is translated to an edn string which is then passed to the Datomic transact API function

Convenience shortcut for :"db.part/tx"

Convenience shortcut for :"db/txInstant"

Convenience shortcut for :"db.type/bigdec"

Convenience shortcut for :"db.type/bigint"

Convenience shortcut for :"db.type/boolean"

Convenience shortcut for :"db.type/bytes"

Convenience shortcut for :"db.type/double"

Convenience shortcut for :"db.type/float"

Convenience shortcut for :"db.type/instant"

Convenience shortcut for :"db.type/keyword"

Convenience shortcut for :"db.type/long"

Convenience shortcut for :"db.type/ref"

Convenience shortcut for :"db.type/string"

Convenience shortcut for :"db.type/uri"

Convenience shortcut for :"db.type/uuid"

Convenience shortcut for :"db/unique"

Convenience shortcut for :"db.unique/identity"

Convenience shortcut for :"db.unique/value"

Convenience shortcut for :"db.part/user"

Convenience shortcut for :"db/valueType"

Types

datom_map :: %{e: integer, a: atom, v: term, tx: integer, added: boolean}
query_option ::
  DatomicGenServer.send_option |
  {:response_converter, (Exdn.exdn -> term)} |
  {:edn_tag_handlers, [{atom, Exdn.handler}, ...]}
transaction_result :: %{"db-before": %{"basis-t": integer}, "db-after": %{"basis-t": integer}, "tx-data": [datom_map], tempids: %{integer => integer}}

Functions

_and(inner_clauses)

Specs

_and([Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating an and clause.

Note that in Datomic, and clauses are only for use inside or clauses; and is the default otherwise.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

Example

Db._and([
    [Db.q?(:org), :"organization/ngo" true],
    [Db.q?(:org), :"organization/country" :"country/france"]
])

sends the following to Datomic:

(and [?org :organization/ngo true]
     [?org :organization/country :country/france])
_expr(function_symbol, remaining_expressions, bindings)

Specs

_expr(atom, [Exdn.exdn], [Exdn.exdn]) :: [{:list, [Exdn.exdn]}]

Convenience shortcut for creating a Datomic expression clause.

An expression clause allows arbitrary Java or Clojure functions to be used inside of Datalog queries; they are either of form [(predicate ...)] or [(function ...) bindings]. An expression clause is thus a Clojure list inside a vector.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

_fn()

Specs

_fn :: :"db/fn"

Convenience shortcut for :"db/fn"

_not(inner_clause)

Specs

_not([Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating a not clause.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this allows us not to have to sprinkle that syntax all over the place.

Example

Db._not([Db.q?(:eid), :"person/age" 13])

sends the following to Datomic:

(not [?eid :person/age 13])
_not_join(binding_list, inner_clause_list)

Specs

_not_join([{:symbol, atom}, ...], [Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating a not-join clause.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

Example

Db._not_join(
  [ Db.q?(:employer) ],
  [ [Db.q?(:employer), :"business/employee" Db.q?(:person)],
    [Db.q?(:employer), :"business/nonprofit" true]
  ]
)

sends the following to Datomic:

(not-join [?employer]
       [?employer :business/employee ?person]
       [?employer :business/nonprofit true])
_or(inner_clauses)

Specs

_or([Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating an or clause.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

Example

Db._or([
    [Db.q?(:org), :"business/nonprofit" true],
    [Db.q?(:org), :"organization/ngo" true]
])

sends the following to Datomic:

(or [?org :business/nonprofit true]
    [?org :organization/ngo true])
_or_join(binding_list, inner_clause_list)

Specs

_or_join([{:symbol, atom}, ...], [Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating an or-join clause.

The first parameter to this function should be a list of bindings; the second the list of clauses.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

Example

Db._or_join(
  [ Db.q?(:person) ],
  [ Db._and([
      [Db.q?(:employer), :"business/employee" Db.q?(:person)],
      [Db.q?(:employer), :"business/nonprofit" true]
    ]),
    [Db.q?(:person), :"person/age" 65]
  ]
)

sends the following to Datomic:

(or-join [?person]
       (and [?employer :business/employee ?person]
            [?employer :business/nonprofit true]))
       [?person :person/age 65])
_pull(arg, pattern_clauses)

Specs

_pull({:symbol, atom}, [Exdn.exdn]) :: {:list, [Exdn.exdn]}

Convenience shortcut for creating a Datomic pull expression.

In Exdn, Clojure lists are represented as tuples with the tag :list, so this function allows us not to have to sprinkle that syntax all over the place.

Example

Db._pull(Db.q?(:e), [:"person/address"])

sends the following to Datomic:

(pull ?e [:person/address])
add()

Specs

add :: :"db/add"

Convenience shortcut for :"db/add"

alter_attribute()

Specs

alter_attribute :: :"db.alter/attribute"

Convenience shortcut for :"db.alter/attribute"

blank_binding()

Specs

blank_binding :: {:symbol, :_}

Convenience shortcut for the blank binding _ as used, for example, in: [:find ?x :where [_ :likes ?x]]

cardinality()

Specs

cardinality :: :"db/cardinality"

Convenience shortcut for :"db/cardinality"

cardinality_many()

Specs

cardinality_many :: :"db.cardinality/many"

Convenience shortcut for :"db.cardinality/many"

cardinality_one()

Specs

cardinality_one :: :"db.cardinality/one"

Convenience shortcut for :"db.cardinality/one"

collection_binding(placeholder_atom)

Specs

collection_binding(atom) :: [{:symbol, atom}, ...]

Convenience shortcut for collection binding find specification ... as used, for example, in: [:find ?e in $ [?a ...] :where [?e age ?a] ]

dbid(db_part)

Specs

dbid(atom) :: {:tag, :"db/id", [atom]}

Convenience function that generates #db/id[ <partition> ]

doc()

Specs

doc :: :"db/doc"

Convenience shortcut for :"db/doc"

entity(server_identifier, exdn, attr_names \\ :all, options \\ [])

Specs

entity(GenServer.server, [Exdn.exdn], [atom] | :all, [query_option]) ::
  {:ok, term} |
  {:error, term}

Issues an entity call to that is passed to the Datomic entity API function.

The first parameter to this function is the pid or alias of the GenServer process; the second is an edn string representing the parameter that is to be passed to entity: either an entity id, an ident, or a lookup ref. The third parameter is a list of atoms that represent the keys of the attributes you wish to fetch, or :all if you want all the entity’s attributes.

The options keyword list may include a :client_timeout option that specifies the milliseconds timeout passed to GenServer.call, and a :message_timeout option that specifies how long the GenServer should wait for a response before crashing (overriding the default value set in start or start_link). Note that if the :client_timeout is shorter than the :message_timeout value, the call will return an error but the server will not crash even if the message is never returned from the Clojure peer.

Example

Db.entity(DatomicGenServer, :"person/email")

# => {ok, %{ Db.ident => :"person/email", 
             Db.value_type => Db.type_string, 
             Db.cardinality => Db.cardinality_one, 
             Db.doc => "A person's email"}}
fn_cas()

Specs

fn_cas :: :"db.fn/cas"

Convenience shortcut for :"db.fn/cas"

fn_retract_entity()

Specs

fn_retract_entity :: :"db.fn/retractEntity"

Convenience shortcut for :"db.fn/retractEntity"

fulltext()

Specs

fulltext :: :"db/fulltext"

Convenience shortcut for :"db/fulltext"

id()

Specs

id :: :"db/id"

Convenience shortcut for :"db/id"

ident()

Specs

ident :: :"db/ident"

Convenience shortcut for :"db/ident"

implicit()

Specs

implicit :: {:symbol, :"$"}

Convenience shortcut for the implicit data source $

inS(placeholder_atom)

Specs

inS(atom) :: {:symbol, atom}

Convenience function to generate Datomic data source specifications—i.e., symbols prefixed by a dollar sign.

Accepts an atom as its argument, representing the symbol to which the dollar sign is to be prepended.

index()

Specs

index :: :"db/index"

Convenience shortcut for :"db/index"

install_attribute()

Specs

install_attribute :: :"db.install/_attribute"

Convenience shortcut for :"db.install/_attribute"

is_component()

Specs

is_component :: :"db/isComponent"

Convenience shortcut for :"db/isComponent"

no_history()

Specs

no_history :: :"db/noHistory"

Convenience shortcut for :"db/noHistory"

q(server_identifier, exdn, options \\ [])

Specs

q(GenServer.server, [Exdn.exdn], [query_option]) ::
  {:ok, term} |
  {:error, term}

Queries a DatomicGenServer using a query formulated as an Elixir list. This query is translated to an edn string which is then passed to the Datomic q API function.

The first parameter to this function is the pid or alias of the GenServer process; the second is the query.

The options keyword list may include a :client_timeout option that specifies the milliseconds timeout passed to GenServer.call, and a :message_timeout option that specifies how long the GenServer should wait for a response before crashing (overriding the default value set in DatomicGenServer.start or DatomicGenServer.start_link). Note that if the :client_timeout is shorter than the :message_timeout value, the call will return an error but the server will not crash even if the response message is never returned from the Clojure peer.

Example

query = [:find, Db.q?(:c), :where, [Db.q?(:c), Db.doc, "A person's name"]]
Db.q(DatomicGenServer, query)

#=> {:ok, #MapSet<['A']>}  # ASCII representation of ID 65
q?(placeholder_atom)

Specs

q?(atom) :: {:symbol, atom}

Convenience function to generate Datomic query placeholders—i.e., symbols prefixed by a question mark.

Accepts an atom as its argument, representing the symbol to which the question mark is to be prepended.

retract()

Specs

retract :: :"db/retract"

Convenience shortcut for :"db/retract"

schema_partition()

Specs

schema_partition :: :"db.part/db"

Convenience shortcut for :"db.part/db"

single_scalar()

Specs

single_scalar :: {:symbol, :.}

Convenience shortcut for the single scalar find specification . as used, for example, in: [:find ?e . :where [?e age 42] ]

transact(server_identifier, exdn, options \\ [])

Specs

transact(GenServer.server, [Exdn.exdn], [DatomicGenServer.send_option]) ::
  {:ok, DatomicGenServer.Db.DatomicTransaction.t} |
  {:error, term}

Issues a transaction against a DatomicGenServer using a transaction formulated as an Elixir list of maps. This transaction is translated to an edn string which is then passed to the Datomic transact API function.

The first parameter to this function is the pid or alias of the GenServer process; the second is the transaction data.

The options keyword list may include a :client_timeout option that specifies the milliseconds timeout passed to GenServer.call, and a :message_timeout option that specifies how long the GenServer should wait for a response before crashing (overriding the default value set in DatomicGenServer.start or DatomicGenServer.start_link). Note that if the :client_timeout is shorter than the :message_timeout value, the call will return an error but the server will not crash even if the response message is never returned from the Clojure peer.

Example

data_to_add = [%{ 
    Db.id => Db.dbid(Db.schema_partition),
    Db.ident => :"person/name",
    Db.value_type => Db.type_string,
    Db.cardinality => Db.cardinality_one,
    Db.doc => "A person's name",
    Db.install_attribute => Db.schema_partition
}]
Db.transact(DatomicGenServer, data_to_add)

# => {:ok, %DatomicGenServer.Db.DatomicTransaction{
        basis_t_before: 1001,   
        basis_t_after: 1002, 
        retracted_datoms: [],
        added_datoms: [
          %DatomicGenServer.Db.Datom{a: 50, added: true, e: 13194139534314, tx: 13194139534314, 
              v: %Calendar.DateTime{abbr: "UTC", day: 15, hour: 3, min: 20, month: 2, sec: 1, std_off: 0, 
                                    timezone: "Etc/UTC", usec: 746000, utc_off: 0, year: 2016}},
          %DatomicGenServer.Db.Datom{a: 41, added: true, e: 65, tx: 13194139534314, v: 35},
          %DatomicGenServer.Db.Datom{a: 62, added: true, e: 65, tx: 13194139534314, v: "A person's name"},
          %DatomicGenServer.Db.Datom{a: 10, added: true, e: 65, tx: 13194139534314, v: :"person/name"},
          %DatomicGenServer.Db.Datom{a: 40, added: true, e: 65, tx: 13194139534314, v: 23},
          %DatomicGenServer.Db.Datom{a: 13, added: true, e: 0, tx: 13194139534314, v: 65}],
        tempids: %{-9223367638809264706 => 65}}}
transaction_partition()

Specs

transaction_partition :: :"db.part/tx"

Convenience shortcut for :"db.part/tx"

tx_instant()

Specs

tx_instant :: :"db/txInstant"

Convenience shortcut for :"db/txInstant"

type_bigdec()

Specs

type_bigdec :: :"db.type/bigdec"

Convenience shortcut for :"db.type/bigdec"

type_bigint()

Specs

type_bigint :: :"db.type/bigint"

Convenience shortcut for :"db.type/bigint"

type_boolean()

Specs

type_boolean :: :"db.type/boolean"

Convenience shortcut for :"db.type/boolean"

type_bytes()

Specs

type_bytes :: :"db.type/bytes"

Convenience shortcut for :"db.type/bytes"

type_double()

Specs

type_double :: :"db.type/double"

Convenience shortcut for :"db.type/double"

type_float()

Specs

type_float :: :"db.type/float"

Convenience shortcut for :"db.type/float"

type_instant()

Specs

type_instant :: :"db.type/instant"

Convenience shortcut for :"db.type/instant"

type_keyword()

Specs

type_keyword :: :"db.type/keyword"

Convenience shortcut for :"db.type/keyword"

type_long()

Specs

type_long :: :"db.type/long"

Convenience shortcut for :"db.type/long"

type_ref()

Specs

type_ref :: :"db.type/ref"

Convenience shortcut for :"db.type/ref"

type_string()

Specs

type_string :: :"db.type/string"

Convenience shortcut for :"db.type/string"

type_uri()

Specs

type_uri :: :"db.type/uri"

Convenience shortcut for :"db.type/uri"

type_uuid()

Specs

type_uuid :: :"db.type/uuid"

Convenience shortcut for :"db.type/uuid"

unique()

Specs

unique :: :"db/unique"

Convenience shortcut for :"db/unique"

unique_identity()

Specs

unique_identity :: :"db.unique/identity"

Convenience shortcut for :"db.unique/identity"

unique_value()

Specs

unique_value :: :"db.unique/value"

Convenience shortcut for :"db.unique/value"

user_partition()

Specs

user_partition :: :"db.part/user"

Convenience shortcut for :"db.part/user"

value_type()

Specs

value_type :: :"db/valueType"

Convenience shortcut for :"db/valueType"