can we alter datomic enums or can we add any new values to datomic enums? - clojure

For example I have following structure
{:db/id #db/id[:db.part/db]
:db/ident :persons/gender
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "A person's gender enum reference"
:db.install/_attribute :db.part/db}
;; :persons/gender enum values
{:db/id #db/id[:db.part/user]
:db/ident :persons.gender/male}
{:db/id #db/id[:db.part/user]
:db/ident :persons.gender/female}
and after designing i want to add one more attribute to existing structure
is it possible with datomic..?

The short answer is yes, you aren't really altering the DB schema, just adding a new allowed value so it doesn't conflict with any existing data. This case is not described as schema alteration as you can check here http://docs.datomic.com/schema.html

You may find that Tupelo Datomic can help with your use case. It is a user-friendly library to make interacting with Datomic easier and more effortless.

Related

datalevin db/id vs db/ident

I want to use Datalevin as a database for my app. The README mentions :db/id as the identifier of entities, and I see these do get an autoincremented integer value on insertion.
However, there are also multiple mentions of :db/ident in the source code, for example for implicit constants.
What are the purposes of the two keywords and what are the differences?
:db/ident are called Entity
Identifiers
in Datomic.
They are used to allow for easier "pointing" to other, well known
things; like an enum. So you can have the enum values as datums, but
still be able to reference them via a keyword (no need to look them up
every time before using them).
E.g.
(def conn (d/create-conn "./tst" {}))
(d/transact! conn
[{:db/id 1, :customer-type/name "Fictional", :db/ident :fictional}
{:db/id 2, :customer/name "ACME", :customer/type :fictional}])
(d/pull #conn
[:db/id :customer/name {:customer/type [:db/id :customer-type/name]}]
2)
; ⇒ {:db/id 2, :customer/name "ACME",
; :customer/type {:db/id :fictional,
; :customer-type/name "Fictional"}}

Trouble implementing tagging in a datomic db

I'm trying to set up a tagging systems in my db, I have added two attrs to handle this:
{:db/ident :metadata/tags
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db/isComponent true}
{:db/ident :metadata/tag
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one
:db/index true}
This works more or less in terms of assigning tags, but how I am encountering some problems in filtering for tags. I think when doing this I assumed that I'd be able to reference the tags the way one would idents, as a keyword, e.g. :tag1. But it's proving a bit more challenging.
My first approach has been to use a query filter expression, something like this (non-working attempt):
'[:find (pull ?doc [*])
:in $ ?tags
:where [?doc :arb/metadata ?meta]
[?doc :metadata/tags ?tagset]
[(every? (set ?tags) ?tagset)]]
where :tags is a supplied list of tag keywords, e.g. [:tag1 :tag2 ...]. But the result of the pull doesn't quite work here because for each tag the pull returns a map including the :db/id as well as the tag e.g.:
[#:arb{:metadata
[{:db/id 17592186045497,
:metadata/tags
[{:db/id 17592186045498, :metadata/tag :tag1}
{:db/id 17592186045499, :metadata/tag :tag2}],
I thought that I could still work with this by mapping over the ?tagset to extract just the tag keywords, but inside the map expression '?tagset` ends up being out of scope. So I'm a bit stumped about how I should approach this problem, which I feel must be somewhat common... Any tips?

Finding the date of the oldest and newest entity with a certain attribute in Datomic?

Let's say a have a Datomic schema like this:
{:db/id #db/id[:db.part/db]
:db/ident :app/createdAt
:db/doc "The date and time when the entity was created (not necessarily the same as tx time)"
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id[:db.part/db]
:db/ident :app/type
:db/doc "The type of the entity"
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
And multiple of such entities are created in the lifetime of the application. I'm interested in finding the :app/createdAt instant/date for the oldest and newest entity of a certain type (:app/type), say "type1". How would such a query look like in Datomic?
An easy way is to use a Datalog query:
[:find (min ?c) (max ?c) :in $ ?type :where
[?e :app/type ?type]
[?e :app/createdAt ?c]]
Performance considerations
As of Datomic 0.9.5385, the Datalog engine will perform a full scan of the entities matching the [?e :app/type ?type] clause; if there are many such entities, this can result in many network roundtrips to storage, high resource consumption on the Peer, and significant latency.
Fortunately, you can use Datomic's Optimization of Range Predicates to restrict the number of datoms scanned by the query. For instance, to compute the maximum creation date, if you know that at least one such entity was created after August 2016, you can call:
(d/q '[:find (max ?c) . :in $ ?type ?lower-bound :where
[?e :app/createdAt ?c]
[(>= ?c ?lower-bound)]
[?e :app/type ?type]]
db #inst "2016-08")
Note that the order of Datalog clauses matters.
Disclaimer: I am not knowledgeable about the source code of Datomic, and am only inferring the above assertions from personal experiments.

inserting multiple ordinality attributes/values to datomic db

So far my add-post-to-datomic method looks like
(defn add-post-to-datomic [title, content, useremail]
(d/transact conn [{:db/id (d/tempid :db.part/user),
:post/title title,
:post/content content,
:author/email useremail}]))
I would really like to add functionality to add a potentially variable number of tags.
In my awesome-schema.edn I have
{:db/id #db/id[:db.part/db]
:db/ident :post/tag
:db/valueType :db.type/string
:db/cardinality :db.cardinality/many
:db/doc "tag applied to a post"
:db.install/_attribute :db.part/db}
So it's okay if there are multiple ones in the db thanks to the cardinality set to many.
How can I write the above method to also work for a variable number of tags ?
Look at the Transactions Docs under "Cardinality many transactions". As long as you pass a set of tags this should work:
(defn add-post-to-datomic [title content user-email tag-set]
(d/transact conn [{:db/id (d/tempid :db.part/user)
:post/title title
:post/content content
:post/tag tag-set
:author/email user-email}]))

Datomic valueType

When trying to persist a list of node entities with a :threshold attribute defined thus in the schema:
{:db/id #db/id[:db.part/db]
:db/ident :node/threshold
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one
:db/fulltext false
:db/doc "Threshold"
:db.install/_attribute :db.part/db}
i get the following error:
CompilerException java.util.concurrent.ExecutionException:
java.lang.IllegalArgumentException:
:db.error/wrong-type-for-attribute Value 90 is not a valid :int
for attribute :node/threshold
I use the following code:
(defn store-tree [tree]
#(d/transact dbconn/conn (into [] (vals tree))))
(store-tree parsed-tree-with-refs)
where tree is a map of node names to nodes.
Curiously enough, i took the EDN for the specific entity with :node threshold 90 from the REPL and manually transact'ed it, and it worked without any problems. I used this code:
#(d/transact dbconn/conn [{:db/id (d/tempid :db.part/user),
:node/threshold 90, :node/location "US"}])
Can someone please help?
Thanks,
Vitaliy.