Datomic Pro and Datomic Cloud are FREE!

Nubank chooses Datomic

Brazil is the world's 5th largest country in both area and population, has the 2nd most airports (behind the United States), and one of the highest credit card interest rates in the world. The Brazilian banking industry is both heavily regulated and extremely concentrated. Enter Nubank, whose founders have created a banking alternative in one of the world’s fastest growing mobile markets.

Traditional software wasn’t going to get the job done for Nubank. From the very beginning, the goal was “differentiation through technology.” They needed to plan for growth, both in size and complexity, to meet constantly changing regulatory and business rules.

Nubank turned to Datomic to access multiple data stores simultaneously, process transactions, and easily query historical data. Engineers can perform complex analyses on real-time data while the performance of the production environment is preserved. Along the way, the Cognitect team helped steer Nubank’s engineers on the right path as they built Datomic into their business.

Read the full case study to see how Datomic and Clojure are helping Nubank change the banking industry. ..

A Conversational Introduction to Datomic

My colleague Carin Meier has been writing up her conversations with Datomic, introducing the database via an imagined dialog between a new user and the database.  In a very short space she introduces

Carin puts all these ideas into context with examples that you can explore in minutes.  If you are new to Datomic, check it out.
..

Datomic Best Practices

We are happy to announce the addition of a Best Practices section to the Datomic Development Resources documentation. The Best Practices topic is a tool to help users quickly find preferred usage patterns when approaching common scenarios in Datomic.

The Datomic Best Practices documentation includes sections on:


Where appropriate, examples are included as are links to the full documentation for further reading.

See the Best Practices documentation for the full list. We expect to add more subtopics to the Best Practices documentation over time, so check back periodically.

We look forward to your feedback. If you have any comments on the new best practices topics, or any questions you think would be addressed by an additional topic, let us know! ..

More Power for Datomic Datalog: Negation, Disjunction, and Range Optimizations

Today's Datomic release includes a number of enhancements to Datomic's Datalog query language:
  • Negation, via the new not and not-join clauses
  • Disjunction (or) without using rules, via the new or and or-join clauses
  • Required rule bindings
  • Improved optimization of range predicates
Each is described below, and you can follow the examples from the mbrainz data set in Java or in Clojure

Negation

The not clause shrinks a result set, removing already-bound tuples whose variables match a set of clauses.  
For example, the following query finds the names of all artists who are not Canadian:
[:find ?name
:where [?eid :artist/name ?name]
(not [?eid :artist/country :country/CA])]
The not-join clause generalizes not, and allows you to also specify which variables inside the not-join will unify with the enclosing query.
The following query counts artists who did not release an album in 1970.  The not-join specifies that ?artist unifies with the containing query, and (implicitly) that ?release is local.
[:find (count ?artist) .
:where [?artist :artist/name]
(not-join [?artist]
[?release :release/artists ?artist]
[?release :release/year 1970])]
As elsewhere in Datomic Datalog, lists of clauses inside not or not-join are connected by an implicit and

Disjunction

Datomic Datalog has long supported disjunction (or) via rules.  Rules are a powerful abstraction mechanism, allowing you to create named pieces of logic to be reused across multiple queries.  Rules are also the path to recursion in query.  But sometimes you don't need all that, and you just want simple disjunction in situ.
The or clause binds variables that match at least one of a set of predicates.
As an example, consider the way mbrainz models release media.   There are four separate enums that all represent vinyl: vinylvinyl-7vinyl-10, andvinyl-12.  An or clause can group these all together, finding all the vinyl releases:
[:find (count ?medium) .
:where (or [?medium :medium/format :medium.format/vinyl7]
[?medium :medium/format :medium.format/vinyl10]
[?medium :medium/format :medium.format/vinyl12]
[?medium :medium/format :medium.format/vinyl])]
The and clause is available as an explicit form inside or clauses.  For example, the following query finds artists that are either groups or female individuals:
[:find (count ?artist) .
:where (or [?artist :artist/type :artist.type/group]
(and [?artist :artist/type :artist.type/person]
[?artist :artist/gender :artist.gender/female]))]
Note that and clauses are used only inside or – elsewhere in query, and is implicit wherever clauses appear sequentially.
The or-join clause is a generalization of or, just as not-join generalizes not.

All Together

Negation and disjunction can be nested, allowing direct expression of e.g. 'not this or that', e.g.
[:find ?name
:where [?eid :artist/name ?name]
(not (or [?eid :artist/country :country/US])
[?eid :artist/country :country/CA])]
which means "find all the artists, except those that are from the US or Canada."
Required Rule Bindings
Rules are relational, and do not dictate the order in which variables are bound.   Imagine a rule that finds tracks shorter than a certain maximum length for an artist.  You might write that rule as:
[(short-track ?a ?t ?len ?max)
[?t :track/artists ?a]
[?t :track/duration ?len]
[(< ?len ?max)]]
This rule does not tell the caller anything about when the four variables might be bound, revealing little about its use.  You might try to
  • invoke the rule in a context where no variables are bound yet, finding all possible relationships
  • invoke the rule with all variables bound, as a predicate check
  • any combination of variable bindings between these two extremes 

Often, complete flexibility in binding is exactly what you want.  But in this case, the author of the rule has a specific intent:
  • ?max is a required input, as nothing inside the rule knows how to manufacture a ?max
  • ?a is an intended input (this rule is about artists)
Rules can specify required vars in a list as the first element after the rule name.  The rule head above can thus be rewritten as
(short-track [?a ?max] ?t ?len)
Now the ?a and ?max variables are required. Datomic will not invoke a rule until required variables are bound, and will throw an exception if they cannot be bound.
Optimization of Range Predicates
Datomic now better leverages indexes to lookup range predicates (=, <=, >=, <. and >) where one argument is bound at the start of the query.  For example, in the following query
[:find (count ?artist) .
:in $ ?max-year
:where [?artist :artist/startYear ?year]
[(< ?year ?max-year)]]

Datomic will see that ?max-year is bound, and use the AVET index on :artist/startYear to consider only datoms guaranteed to match the predicate.  This can result in substantial speedups and reduced memory usage for queries that substantially limit results via range predicates.

Summary

Taken together, the new capabilities above add substantial expressive power and performance to Datomic Datalog.  Get the full details in the docs, and happy querying!







..

Datomic Pull API

Datomic's new Pull API is a declarative way to make hierarchical selections of information about entities. You supply a pattern to specify which attributes of the entity (and nested entities) you want to pull, and db.pull returns a map for each entity.

Pull API vs. Entity API

The Pull API has two important advantages over the existing Entity API:

Pull uses a declarative, data-driven spec, whereas Entity encourages building results via code. Data-driven specs are easier to build, compose, transmit and store. Pull patterns are smaller than entity code that does the same job, and can be easier to understand and maintain.

Pull API results match standard collection interfaces (e.g. Java maps) in programming languages, where Entity results do not. This eliminates the need for an additional allocation/transformation step per entity.

Wildcards

A pull pattern is a list of attribute specifications.  If you want all attributes, you can use the wildcard (*) specification along with an entity identifier.  (In the examples below, entity identifiers such as led-zeppelin are variables defined in the complete code examples in Clojure and Java.)

;; Clojure API
(d/pull db '[*] led-zeppelin)

;; Java API
db.pull("[*]", ledZeppelin)

A pull result is a map per entity, shown here in edn:

;; result
{:artist/sortName "Led Zeppelin",
:artist/name "Led Zeppelin",
:artist/type {:db/id 17592186045746},
:artist/country {:db/id 17592186045576},
:artist/gid #uuid "678d88b2-87b0-403b-b63d-5da7465aecc3",
:artist/endDay 25,
:artist/startYear 1968,
:artist/endMonth 9,
:artist/endYear 1980,
:db/id 17592186050305}

Attributes

You can also specify the attributes you want explicitly, as with :artist/name and :artist/gid below:

;; pattern
[:artist/name :artist/gid]

;; input
led-zeppelin

;; result
{:artist/gid #uuid "678d88b2-87b0-403b-b63d-5da7465aecc3",
:artist/name "Led Zeppelin"}

The underscore prefix reverses the direction of an attribute, so :artist/_country pulls all the artists for a particular country:

;; pattern
[:artist/_country]

;; input
greatBritain

;; result
{:artist/_country [{:db/id 17592186045751}
{:db/id 17592186045755}
...]}

Components

Datomic component attributes are pulled recursively by default, so the :release/media pattern below automatically returns a release's tracks as well:

;; pattern
[:release/media]

;; input
darkSideOfTheMoon

;; result
{:release/media
[{:db/id 17592186121277,
:medium/format {:db/id 17592186045741},
:medium/position 1,
:medium/trackCount 10,
:medium/tracks
[{:db/id 17592186121278,
:track/duration 68346,
:track/name "Speak to Me",
:track/position 1,
:track/artists [{:db/id 17592186046909}]}
{:db/id 17592186121279,
:track/duration 168720,
:track/name "Breathe",
:track/position 2,
:track/artists [{:db/id 17592186046909}]}
{:db/id 17592186121280,
:track/duration 230600,
:track/name "On the Run",
:track/position 3,
:track/artists [{:db/id 17592186046909}]}
...]}]}

Map Specifications

Instead of just an attribute name, you can use a nested map specification to pull related entities.  The pattern below pulls the :db/id and :artist/name of each artist:

;; pattern
[:track/name {:track/artists [:db/id :artist/name]}]

;; input
ghostRiders

;; result
{:track/artists [{:db/id 17592186048186, :artist/name "Bob Dylan"}
{:db/id 17592186049854, :artist/name "George Harrison"}],
:track/name "Ghost Riders in the Sky"}

And of course everything nests arbitrarily, in case you need the release's medium's track's names and artists:

 ;; pattern
[{:release/media
[{:medium/tracks
[:track/name {:track/artists [:artist/name]}]}]}]

;; input
concertForBanglaDesh

;; result
[{:medium/tracks
[{:track/artists
[{:artist/name "Ravi Shankar"} {:artist/name "George Harrison"}],
:track/name "George Harrison / Ravi Shankar Introduction"}
{:track/artists [{:artist/name "Ravi Shankar"}],
:track/name "Bangla Dhun"}]}
{:medium/tracks
[{:track/artists [{:artist/name "George Harrison"}],
:track/name "Wah-Wah"}
{:track/artists [{:artist/name "George Harrison"}],
:track/name "My Sweet Lord"}
{:track/artists [{:artist/name "George Harrison"}],
:track/name "Awaiting on You All"}
{:track/artists [{:artist/name "Billy Preston"}],
:track/name "That's the Way God Planned It"}]
...]}

Try It Out

The Pull API has many other capabilities not shown here.  See the full docs for defaults, limits, bounded and unbounded recursion, and more.  Or check out the examples in Clojure and Java.
..


8 of 14