Fork me on GitHub
#datomic
<
2020-10-02
>
zane17:10:58

Is it possible to pull all attributes, but with a default? I’m imagining something like:

(pull [(* :default :no-value)]  …

kenny17:10:30

I don't see how that could be possible. "*" means all attributes that an entity has so there can't be a default.

kenny17:10:16

i.e., there is no info on what an entity does not have.

zane17:10:49

Let me try to explain how I would do it in two queries.

zane17:10:18

(let [attributes (d/q '[:find [?a ...]
                          :where
                          [?e ?a _]]
                        db)
        pattern (mapv (fn [attribute]
                        `(~attribute :default :no-value))
                      attributes)]
    (d/q `[:find (pull $ ~pattern ?e)
           :in $
           :where
           [?e _ _]]
         db))

zane17:10:23

Something along those lines.

kenny21:10:35

Definitely not something built in. I'd advise against that. What is your use case?

donyorm17:10:21

So I have the following query:

{:query
 {:find [?e],
  :in [$ ?string-val-0],
  :where
  [(or-join
    [?e]
    (and
     [?e :exception/message ?message-0]
     [(.contains ?message-0 ?string-val-0)])
    (and
     [?e :exception/message ?explanation-0]
     [?explanation-0 :message/explanation ?explanation-val-0]
     [(.contains ?message-0 ?string-val-0)]))]},
 :args
 [#object[compute.datomic_client_memdb.core.LocalDb 0x2a589f86 "compute.datomic_client_memdb.core.LocalDb@2a589f86"]
  "abc"]}

donyorm17:10:49

But I'm getting Execution error (Exceptions$IllegalArgumentExceptionInfo) at datomic.error/arg (error.clj:57). :db.error/insufficient-binding [?string-val-0] not bound in expression clause: [(.contains ?message-0 ?string-val-0)], and I'm not sure why

zane17:10:34

If you want ?string-val-0 to unify the outer clause you’ll need to include it in the rules-vars vector: (or-join [?e ?string-val-0] …)

zane17:10:39

At least I suspect that’s what’s wrong.

donyorm17:10:36

Is it not enought to have ?string-val-0 defined in the in list?

nando22:10:27

I'm trying to figure out how to format db.type/instant values, for display in a UI. Using clojure.java-time as follows: (:require [java-time :as t]) evaluating (t/format #inst "2020-09-26T23:08:27.619-00:00") returns "Sun Sep 27 01:08:27 CEST 2020" but if I add a custom format (t/format "dd/MM/yyyy" #inst "2020-09-26T23:08:27.619-00:00") I get the error Execution error (ClassCastException) at java-time.format/format (format.clj:50). java.util.Date cannot be cast to java.time.temporal.TemporalAccessor

nando22:10:24

Any suggestions for formatting db.type/instant values ?

favila23:10:03

You need to coerce the Java.util.date to an instant

nando23:10:11

(t/instant #inst "2020-09-26T23:08:27.619-00:00") => #time/instant "2020-09-26T23:08:27.619Z"

nando23:10:32

(t/format "yyyy/MM/dd" (t/instant #inst "2020-09-26T23:08:27.619-00:00")) => Execution error (UnsupportedTemporalTypeException) at java.time.Instant/getLong (Instant.java:603). Unsupported field: YearOfEra

nando23:10:51

the clojure.java-time docs for t/instant say this function "Creates an Instant" https://cljdoc.org/d/clojure.java-time/clojure.java-time/0.3.2/api/java-time.temporal#instant

nando00:10:23

(t/instant? (.toInstant #inst "2020-09-26T23:08:27.619-00:00")) => true (t/instant? (t/instant #inst "2020-09-26T23:08:27.619-00:00")) => true

nando00:10:34

This works: (t/format ".dd" (t/zoned-date-time 2015 9 28)) => "2015.09.28" but I can't find a way to convert a datomic db.type/instant to a zoned-date-time.

nando00:10:37

There must be a more straightforward way to format datomic datetime values.

seancorfield04:10:06

@U078GPYL8

user=> (t/format "dd/MM/yyyy" (t/zoned-date-time #inst "2020-09-26T23:08:27.619-00:00" (t/zone-id "UTC")))
"26/09/2020"
user=> 

seancorfield04:10:27

(or whatever TZ you need there)

seancorfield04:10:42

Although if you're dealing with #inst which I believe are just regular java.util.Date objects, this should work (without clojure.java-time at all):

user=> (let [f (java.text.SimpleDateFormat. "dd/MM/yyyy")]
         (.format f #inst "2020-09-26T23:08:27.619-00:00"))
"26/09/2020"
user=> 

seancorfield04:10:11

Yup, Datomic docs say it's just a java.util.Date:

:db.type/instant	instant in time	java.util.Date	#inst "2017-09-16T11:43:32.450-00:00"

nando10:10:36

@U04V70XH6 Thanks very much! I've confirmed that both approaches work as expected with an #inst returned from datomic.

nando11:10:18

I've learned a lot here, both by dipping my toes into the clojure.java-time and tick libraries, and getting a more practical sense of how java interop works through your example.

favila12:10:27

@U078GPYL8 I meant something like this (sorry, was on a phone earlier):

(let [d #inst"2020-10-03T12:18:02.445-00:00"
      f (-> (java.time.format.DateTimeFormatter/ofPattern "dd/MM/yyyy")
            (.withZone (java.time.ZoneId/systemDefault)))]
  (.format f (.toInstant d)))

favila12:10:50

I avoid java.text.SimpleDateFormat because it’s the “old” way and it’s not thread-safe

favila12:10:28

I think what sean posted is nearly the equivalent, except he coerces to a zoned date time instead of specifying the zone in the formatter

favila12:10:46

but I’m not familiar with clojure.java-time, I just use java.time directly

nando13:10:48

@U09R86PA4 I see what you originally meant evaluating

(let [d #inst"2020-10-03T12:18:02.445-00:00"
                       f (-> (java.time.format.DateTimeFormatter/ofPattern "dd/MM/yyyy")
                             (.withZone (java.time.ZoneId/systemDefault)))]
                   (.format f d))

nando13:10:13

The same error is produced without the date being wrapped in a .toInstant call.

nando13:10:30

In what type of use case would the fact that SimpleDateFormat is not thread safe produce an unexpected result, particularly in the context of a web application?

favila14:10:52

Def the format object and then use it in functions running in my tools threads

favila14:10:04

There are a few strata of Java date systems

favila14:10:50

The oldest is Java.util.date objects. The newest is Java.time.*, which represents instants as Java.time.Instant objects instead.

favila14:10:16

There are some in between that aren’t worth learning anymore

seancorfield17:10:04

Yup, going via Java Time is definitely the safest route and the best set of APIs to learn. At work, over the past decade we've gone from java.util.Date to date-clj (date arithmetic for that old Date type), to clj-time (wrapping Joda Time), to Java Time (with clojure.java-time in some parts of the code and plain interop in a lot of places). Converting java.util.Date to java.time.Instant and doing everything in Java Time is a bit painful/verbose, but you can write utility functions for stuff you need frequently to hide that interop/verbosity.

favila23:10:38

toInstant method