inserting multiple ordinality attributes/values to datomic db - clojure

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}]))

Related

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?

Inconsistency between in (mem) and out (dev,sql,etc) of process Transactor in Datomic:

I've discovered an inconsistency between the way db.type/instant values are handled in the in (mem) and out (dev,sql...) of process transactor.
When using the in process mem transactor the underlying type of a db.type/instant value is preserved when transacting and retrieving. For example, (type-printer uri) below shows that a transacted java.util.TimeStamp is retrieved as a java.sql.TimeStamp. However in the out of process transactors it is retrieved as a java.util.Date.
In most cases this inconsistency does not cause problems, however when dealing with bad Date implementations it can cause all sorts of problems. In my case the fact that java.sql.TimeStamp does not have a symmetric equals method, meant that a bug in my code only appeared when I ran it in an out of process transactor...
Obviously Datomic does not have any control over external implementations of java.util.Date, so I think the 'Date sanitisation' approach of the out of process transactors is correct.
Really all I want is a sanity check before I raise it as an issue with the Datomic guys so if someone could take a look and see if you agree it would be greatly appreciated.
Thanks,
Matt.
(require '[datomic.api :as d])
(import '(java.sql Timestamp))
(defn type-printer [uri]
"Prints the type of a transacted java.sql.TimeStamp when retrieved from the databse"
(d/create-database uri)
(let [ts-att-schema {:db/id (d/tempid :db.part/db)
:db/ident :entity/ts
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
ts (Timestamp. 1000000000000)
entity {:db/id (d/tempid :db.part/user)
:entity/ts ts}
conn (d/connect uri)]
;transact schema and then entity with associated TimeStamp value
#(d/transact conn [ts-att-schema])
#(d/transact conn [entity])
(let [type (->> (d/db conn)
(d/q '[:find ?ts :where[?e :entity/ts ?ts]])
(first)
(first)
(type)
)]
(println "java.sql.TimeStamp is retrived as type" type))))
;TimeStamp type is preserved when being transacted and then retrieved
(type-printer "datomic:mem://test-db")
;TimeStamp is sanitised in the database and retrieved as a java.util.Date. This case obviously relies on a
;dev transactor running on localhost:4334. Have only verified for dev and sql transactors.
(type-printer "datomic:dev://localhost:4334/test-db")

Inserting data into Datomic partition

Looking at the tutorial documents, I've tried creating a partition by loading my schema from a file, which, among other things, contains the following:
{:db/id #db/id[:db.part/db],
:db/ident :account,
:db.install/_partition :db.part/db}
If I try inserting data with the following:
(d/transact conn
[{:db/id #db/id[:db.part/user -1]
:validation/email email
:validation/code code}])
Everthing works as expected. But if I replace "user" with my partitions name "account", like this:
(d/transact conn
[{:db/id #db/id[:db.part/account -1]
:validation/email email
:validation/code code}])
I get this error:
IllegalArgumentExceptionInfo :db.error/not-a-db-id Invalid db/id: #db/id[:db.part/account -1] datomic.error/arg (error.clj:57)
What am I doing wrong? How can I even be sure that I've created the partitions?
your partition name is :account, not :db.part/account.
this code below should work.
(d/transact conn
[{:db/id #db/id[:account -1]
:validation/email email
:validation/code code}])
You can query for list of installed partitions like this
(d/q '[:find [?ident ...]
:where
[?e :db/ident ?ident]
[_ :db.install/partition ?e]]
db)
=> [:account :db.part/tx :db.part/user]

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

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.

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.