Fork me on GitHub
#datomic
<
2020-01-16
>
dmarjenburgh09:01:22

I have a schema attribute with db.type/float. When I try to transact the value 3.14, it actually transacts 3.140000104904175. What is going on?

Linus Ericsson12:01:12

The problem can be in some serialization, where the float converts to a bit representation (perhaps in fressian if that is used) and then back. The answer is not wrong per se (because floats are imprecise) but I understand you find it surprising. If you really want to use 3.14 as 3.14, use BigDecimal and 3.14M or try if Double is serialized in a more precise way. I’m not sure it will work either but worth a try.

Linus Ericsson12:01:17

(:db.type/decimal and :db.type/double)

Linus Ericsson12:01:40

(I have no insight in the Datomic code base)

dmarjenburgh07:01:25

It doesn't lose precision with doubles. Unfortunately, changing float to double is not a supported schema alteration.

eggsyntax14:01:42

This is a common issue with floating point. Unless you're doing something where exact precision is critical (typically currency or some scientific work) I'd say the standard solution is to round it at the point of display -- as you can see from the example, the difference is so tiny that you could round to anywhere from 2-8 digits and get the right answer. As @UQY3M3F6D points out, using BigDecimal avoids the problem entirely, at the cost of being a) computationally much more expensive/slow, and b) annoying to work with (although less so in Clojure than in Java). Here's a well-known & useful article on the topic: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

timcreasy17:01:29

I noticed the other day that datomic.client.api/q doesn’t support query on a collection of facts:

(d/q '[:find ?name
       :where [_ :person/name ?name]]
     [[1 :person/name "Tim" 42]
      [2 :person/name "Jasmin" 42]])

Execution error (ExceptionInfo) at datomic.client.api.impl/incorrect (impl.clj:42).
Query args must include a database
I remember this being supported in on-prem: https://docs.datomic.com/on-prem/query.html#database-of-facts Is there any way/talk to support this?

timcreasy17:01:34

Looks like datomic.client.api/q requires an argument to satisfy Queryable

timcreasy17:01:12

Was always just handy for small examples 🙂

m0smith17:01:41

We are using a :db/ident as an enum. in Datomic Cloud. When I do a :find (pull ?e [*]) I get the :db/id but not the actual keyword. What is the correct way to implement enums in Datomic in the year 2020?

m0smith17:01:17

Yes, exactly. The problem is, how to return :country/JP from a query rather than just use it in a where clause.

Joe Lane17:01:57

what happens when you do :find (pull ?e [* {:artist/country [*]}])

m0smith17:01:01

sometimes I get {:db/id 777} and other times I get {:db/id 777 db/ident country JP}

m0smith17:01:17

That smiley is a : d

m0smith17:01:55

Is there any disadvange in use a keyword type instead? It simplifies things for sure.

Joe Lane18:01:03

I cant say for sure what the disadvantages would be, however I think modeling things that way sounds great if it meets your applications needs.

fjolne18:01:01

@U050VTWMB what’s wrong with querying for :db/ident specifically? using asterisk is considered to be anti-pattern for production queries. I agree that keywords are more convenient in some cases, but idented entities have a few pros: a) they’re entities thus extensible with additional data b) misspellings are validated on-write by db itself

m0smith18:01:36

What would that query look like?

fjolne18:01:02

[{:artist/country [:db/ident]}]

favila19:01:50

Using a keyword for enums: 1) enum set is “open” (you don’t need to transact ahead of time to create another enum) 2) you get a keyword value in pulls

favila19:01:21

Using a ref for enums: 1) enum set is “closed” (you must transact ahead of time) 2) slightly more efficient storage 3) indexing by value is automatic 4) you can annotate enum entities with additional assertions (they are “just” entities after all. For e.g. you can document/enforce attribute range and and enum set membership with more attributes) 5) pull gives you {:db/ident :keyword-value}

favila19:01:51

I think the usual answer is to post-process the pull result with e.g. clojure-walk to convert to a keyword

m0smith19:01:49

Replacing the * with :db/ident seemed to fix the problem I was seeing. I just wish the extra :db/ident wasn't in the response as it makes it just another thing to remember