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@nando you can use #specter with something like (transform [(walker :batch-item/nutrient) :batch-item/nutrient] (partial sort-by :nutrient/category) (d/pull ...))
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 ...
#specter will help you to "find some sub-structure and transform it without change anything outside it'
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)
in this case the transform function will not by sort-by :nutrient/category
, but something like #(sort-by (fn [el] ((juxt ..) el)) %)
the path is [(walker :batch-item/nutrient) :batch-item/nutrient ALL]
TLDR; #datomic do not do anything about sorting
Thanks @souenzzo , will look into #specter next.
Does datomic query syntax allow for group-by? I wanted to group some datums by date and then count them.
I've been looking at the clojure core function group-by for this https://clojuredocs.org/clojure.core/group-by
Has it worked ? I will try it as well .. see what happens .. good idea
I haven't incorporated it into the app I'm working on, but it certainly worked in the REPL
(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.right, that is doing group-by after the query .. i meant in the query itself ..
It is my understanding that sorting and grouping is done with clojure functions rather than datalog query syntax.
i reckon so. there are some other aggregate function like max, min count, but not group-by or sort-by that are built in
Have you tried to sort the results of a query yet?
oh sure, tis easy
https://docs.datomic.com/cloud/query/query-data-reference.html#aggregate-example
^^^
Is this not what you mean when you say group-by?
@dansudol '[:find ?date (count ?e) :where [?e :entity/date ?date]]
Have you looked at https://docs.datomic.com/cloud/query/query-data-reference.html#aggregate-example
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
Are the date ranges contiguous and non-overlapping?
yes
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 )
What datomic system are you using?
cloud
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.
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
i guess the count by date is kinda grouping dates in a way so there is the element of group by there
'[: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]
Consider the above a sketch, written in slack, untested, likely need to add a few things.
that is pretty hillarious Joe, nifty idea , i will hack around it
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]]
(You might need to use :with ?month
in that query, I'd have to think about it...)
pretty much, your idea is good .. me like
interesting @lanejo01 .. this works, very nice ( i made my own database function as you suggested ) slick !
Great to hear!