Fork me on GitHub
#datomic
<
2020-09-27
>
nando13:09:35

I'm trying to work out how to sort a collection of items nested within the data structure returned from a pull pattern, particularly a pull that uses a reverse lookup. Here's the pull pattern I'm working with:

[:db/id
 {:batch/formula [:db/id :formula/name]}
 :batch/doses
 :batch/date
 {:batch-item/_batch [:db/id
                      {:batch-item/nutrient [:nutrient/name
                                             {:nutrient/category [:category/sort-order]}]}
                      :batch-item/weight
                      :batch-item/complete?]}]
The :batch-item/_batch bit returns a rather large collection and I want to sort it by :category/sort-order and :nutrient/name

souenzzo13:09:59

@nando you can use #specter with something like (transform [(walker :batch-item/nutrient) :batch-item/nutrient] (partial sort-by :nutrient/category) (d/pull ...))

👍 3
nando13:09:35

So I would wrap the pull in a specter transform? With a query that returns a flat structure, I'd use

(sort-by (juxt :sort-order :nutrient-name)
           (d/q ...

souenzzo14:09:20

#specter will help you to "find some sub-structure and transform it without change anything outside it'

souenzzo14:09:02

once you find what you want to transform (the second argument, know as 'path'. On the example: find a map with this key, and 'enter' this key)

souenzzo14:09:54

in this case the transform function will not by sort-by :nutrient/category, but something like #(sort-by (fn [el] ((juxt ..) el)) %)

souenzzo14:09:21

the path is [(walker :batch-item/nutrient) :batch-item/nutrient ALL] TLDR; #datomic do not do anything about sorting

nando14:09:46

Thanks @souenzzo , will look into #specter next.

daniel.spaniel18:09:26

Does datomic query syntax allow for group-by? I wanted to group some datums by date and then count them.

nando19:09:15

I've been looking at the clojure core function group-by for this https://clojuredocs.org/clojure.core/group-by

daniel.spaniel19:09:19

Has it worked ? I will try it as well .. see what happens .. good idea

nando19:09:58

I haven't incorporated it into the app I'm working on, but it certainly worked in the REPL

nando19:09:42

(defn group-nutrients-by-category
  [v]
  (group-by :category-name v))
I've got a datomic query that returns nutrients, and each of these have a category, such as Vitamins, Minerals, Amino Acids, Plant Extracts. I just tried the above, using (group-nutrients-by-category (find-all-nutrients)) and it worked perfectly, as expected.

daniel.spaniel19:09:11

right, that is doing group-by after the query .. i meant in the query itself ..

nando19:09:34

It is my understanding that sorting and grouping is done with clojure functions rather than datalog query syntax.

daniel.spaniel19:09:51

i reckon so. there are some other aggregate function like max, min count, but not group-by or sort-by that are built in

nando19:09:39

Have you tried to sort the results of a query yet?

daniel.spaniel19:09:55

oh sure, tis easy

Joe Lane19:09:13

Is this not what you mean when you say group-by?

Joe Lane19:09:27

@dansudol '[:find ?date (count ?e) :where [?e :entity/date ?date]]

daniel.spaniel19:09:23

yes, that is pretty close to the query i need Joe, interesting .. i guess if that does the same as group by ( i am reading the examples now ) then that does it .. i am going for something a big more complicated ( count by date range ) but if this works as grouping by date then i am super close to what i want

Joe Lane19:09:02

Are the date ranges contiguous and non-overlapping?

daniel.spaniel19:09:54

beginning ->end of a month , so finding items whose dates are in that range and counting them up, where let's say the range is a year, so each month, wanted the count of the items ( that have date field on them )

Joe Lane19:09:31

What datomic system are you using?

nando19:09:23

If I'm understanding the difference correctly, using group-by will return all records, while using count in an aggregate query will return a single record for each date.

daniel.spaniel19:09:44

you can't use group-by in the query though, just to operate on the returned data , but the last part is right i reckon

daniel.spaniel19:09:41

i guess the count by date is kinda grouping dates in a way so there is the element of group by there

Joe Lane19:09:55

'[:find ?month (count ?e)
  :where
  [(java.time.ZoneId/of "UTC") ?UTC]
  [?e :entity/date ?date]
  [(.toInstant ^Date ?date) ?inst]
  [(.atZone ^Instant ?inst ?UTC) ?inst-in-zone]
  [(.getMonthValue ^ZonedDateTime ?inst-in-zone) ?month]

Joe Lane19:09:47

Consider the above a sketch, written in slack, untested, likely need to add a few things.

daniel.spaniel19:09:20

that is pretty hillarious Joe, nifty idea , i will hack around it

Joe Lane20:09:04

The instant type in datomic is a java.util.Date, so if you want to use the nice .getMonthValue method you'll need some combination of that. There are several other things you could do like make a custom query function to do all the gnarly time conversion stuff in an isolated way. https://docs.datomic.com/cloud/query/query-data-reference.html#deploying Other than that time conversion stuff, this is a pretty trivial query, right? It's basically:

'[:find ?month (count ?e)
  :where
  [?e :entity/date ?date]
  [(my.ions/date->month ?date) ?month]]

Joe Lane20:09:21

(You might need to use :with ?month in that query, I'd have to think about it...)

daniel.spaniel20:09:40

pretty much, your idea is good .. me like

daniel.spaniel20:09:02

interesting @joe.lane .. this works, very nice ( i made my own database function as you suggested ) slick !

Joe Lane20:09:24

Great to hear!