This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-08-16
Channels
- # babashka (53)
- # beginners (61)
- # biff (2)
- # cherry (15)
- # cider (28)
- # clj-kondo (17)
- # clojure (57)
- # clojure-europe (27)
- # clojure-nl (13)
- # clojure-norway (6)
- # clojure-uk (4)
- # clojurescript (30)
- # core-async (2)
- # data-science (39)
- # datomic (16)
- # docker (34)
- # emacs (4)
- # events (1)
- # figwheel-main (9)
- # guix (5)
- # hiccdown (2)
- # honeysql (5)
- # hyperfiddle (5)
- # interceptors (1)
- # jobs (2)
- # joyride (5)
- # lsp (36)
- # midje (1)
- # minimallist (1)
- # nbb (21)
- # off-topic (45)
- # polylith (42)
- # rum (1)
- # shadow-cljs (24)
- # sql (1)
- # squint (62)
- # vrac (1)
- # xtdb (6)
@jarrodctaylor first, thanks for the amazing Max Datom tutorial. I'm working on Level 12 and got stuck on this error :
clojure.lang.ExceptionInfo: 'comment-count-str' needs to be listed under :xforms in datomic/ion-config.edn {:cognitect.anomalies/category :cognitect.anomalies/forbidden, :cognitect.anomalies/message "'comment-count-str' needs to be listed under :xforms in datomic/ion-config.edn"}
Reading :xform
option docs, it seems that the function indeed needs to be in specified in the config file - I thought it's gonna be there,
but maybe this restriction is something that was only added after the course was created?Here's my query:
(d/q '[:find (pull ?posts [{:post/author [:user/first+last-name]}
[:post/comments :xform comment-count-str]])
:where [?posts :post/author _]]
db)
I'm also facing the same problem when running on my machine: https://github.com/jumarko/datomic-starter-sample/pull/10
Found the answer here: https://www.reddit.com/r/Clojure/comments/tw66df/comment/i3kldxb/ The function call must be fully qualified - that feels a bit odd, compared to normal clojure function calls...
Glad you got it sorted out. Thanks for working through the app 🙂
Hi! What is the best way to make optional filtering in query? The Desired behavior is when vector with user-group/uid
s is empty or nil - all users are selected, otherwise select users in provided groups.
(d/q '[:find ?uid
:in $ [?ugi-uid ...]
:where
[?u :user/uid ?uid]
[?ug :user-group/users ?u]
[?ug :user-group/uid ?ugi-uid]]
(user/ctx-db) [#uuid "622f8a38-91b6-4907-b281-62db36568454"])
Just have two paths, one with filtering and one without. Resulting in two clear queries
We’ve used the fact that the query is data to construct the query in code based on desired filters.
i recommend simply writing a different function for each situation, and not trying to invent query-as-data composition logic about it. you'll thank me later 🙂
;; original
(d/q '[:find ?uid
:in $ [?ugi-uid ...]
:where
[?u :user/uid ?uid]
[?ug :user-group/uid ?ugi-uid]
[?ug :user-group/users ?u]]
(user/ctx-db) [#uuid "622f8a38-91b6-4907-b281-62db36568454"])
;; this is slow; it'll find ALL the users, then find all the matching groups, and then finally filter ALL users by those groups.
;; for performance; we should restrict the scope as early as possible to reduce the number of datoms that are considered;
;; first find the groups that match the ids, then find all the users that are in those groups, and then only return those users' ids.
(d/q '[:find ?uid
:in $ [?ugi-uid ...]
:where
[?ug :user-group/uid ?ugi-uid]
[?ug :user-group/users ?u]
[?u :user/uid ?uid]]
(user/ctx-db) [#uuid "622f8a38-91b6-4907-b281-62db36568454"])
;; when no groups are specified, the query is a lot simpler:
(d/q '[:find ?uid
:in $
:where
[?u :user/uid ?uid]]
(user/ctx-db))
;; or far more simply (i assume :user/uid is indexed):
(map :v (d/datoms (user/ctx-db) :avet :user/uid))
;; so the final code would be two functions:
(defn user-ids-by-groups [db group-ids]
(d/q '[:find ?uid
:in $ [?ugi-uid ...]
:where
[?ug :user-group/uid ?ugi-uid]
[?ug :user-group/users ?u]
[?u :user/uid ?uid]]
db group-ids))
(defn all-user-ids [db]
(map :v (d/datoms db :avet :user/uid)))
@U0509NKGK That works in simple cases, if I have 2+ conditions I will struggling with it
I've been using d/query which takes a map as input with parameters as :query :where And then I'm putting it together on the search input variables with cond->. I'm doing something like this essentially:
(defn add-query [org new]
(merge-with into org new))
(comment
(let [a 1
b nil
c 3]
(into (sorted-map)
(cond->
'{:find [[(pull ?e pattern) ...]]
:in [$ pattern]
:where []
:args []}
(some? a) (add-query {:where ['[?e :e/a ?a]]
:in ['?a]
:args [a]})
(some? b) (add-query {:where ['[?e :e/b ?b]]
:in ['?b]
:args [b]})
(some? c) (add-query {:where ['[?e :e/c ?c]]
:in ['?c]
:args [c]})))))
Note that the actual params for d/query is slightly different: https://docs.datomic.com/on-prem/clojure/index.html#datomic.api/query The above was just an example, but should be easily adaptable to real world datomic.api/query usage
Hope that helps 🙂
Be careful with your queries though. I've been OOMing in production too many times.
Full working example here:
(ns backend.datomic-search-demo
(:require [datomic.api :as d]))
(def conn
(let [uri (str "datomic:")]
(d/delete-database uri)
(d/create-database uri)
(d/connect uri)))
(def schema
[#:db{:ident :e/a, :cardinality :db.cardinality/one, :valueType :db.type/long}
#:db{:ident :e/b, :cardinality :db.cardinality/one, :valueType :db.type/long}
#:db{:ident :e/c, :cardinality :db.cardinality/one, :valueType :db.type/long}
#:db{:ident :e/name, :cardinality :db.cardinality/one, :valueType :db.type/string}])
@(d/transact conn schema)
(defn add-query [org new]
(merge-with into org new))
@(d/transact conn [{:e/a 1
:e/b 2
:e/name "First"}
{:e/c 3
:e/b 2
:e/a 1
:e/name "Second"}])
(defn make-query [{:keys [a b c]}]
(let [query (cond->
'{:find [[(pull ?e [:*]) ...]]
:in [$]
:where []
:args []}
(some? a) (add-query {:where ['[?e :e/a ?a]]
:in ['?a]
:args [a]})
(some? b) (add-query {:where ['[?e :e/b ?b]]
:in ['?b]
:args [b]})
(some? c) (add-query {:where ['[?e :e/c ?c]]
:in ['?c]
:args [c]}))]
{:args (:args query)
:query (select-keys query [:find :in :where])}))
(comment
(-> (make-query {:a 1})
(update :args (partial into [(d/db conn)]))
(d/query)))
(comment
(-> (make-query {:a 1 :c 3})
(update :args (partial into [(d/db conn)]))
(d/query)))
Hope that helps @U4U68ADKRThx @UGJE0MM0W! My current solution is similar - just build query map with cond->
. Nice tip - use add-query
- I'll take it)