This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-12-24
Channels
- # adventofcode (25)
- # asami (39)
- # beginners (39)
- # biff (12)
- # clojure (53)
- # clojure-dev (4)
- # clojure-europe (6)
- # clojure-hungary (1)
- # clojure-norway (4)
- # clojure-spec (3)
- # conjure (2)
- # cursive (1)
- # dev-tooling (9)
- # emacs (4)
- # introduce-yourself (2)
- # juxt (4)
- # membrane (8)
- # off-topic (3)
- # polylith (8)
- # portal (4)
- # releases (1)
- # scittle (9)
- # sql (11)
- # squint (5)
- # xtdb (12)
Howdy, I'm running into this error when I attempt to write to a db field that's modeled as an`:enum` in Malli. Partial stack trace
[qtp1095227075-25] ERROR com.biffweb.impl.middleware - Exception while handling request
clojure.lang.ExceptionInfo: Doc wouldn't be a valid :game after transaction. {:tx-doc {:db/doc-type :game, :xt/id #uuid "1ce03e5a-6f0f-460f-bdf6-52f2d2ac6572", :game/name "Tetris", :game/difficulty "hard"}, :explain {:game/difficulty ["invalid type"]}}...
Form
(def game-form [_]
...
[:select
{:name "difficulty"}
[:option {:value :easy} "easy"]
[:option {:value :medium} "medium"]
[:option {:value :hard} "hard"]]
...
Form Handler
(defn new-game [{:keys [params] :as req}]
(biff/submit-tx req
[{:db/doc-type :game
:xt/id (random-uuid)
:game/name (:name params)
:game/difficulty (:difficulty params)}])})
Schema
...
:game/id :uuid
:game [:map {:closed true}
[:xt/id :game/id]
[:game/name :string]
[:game/difficulty [:set [:enum :easy :medium :hard]]]]
...
I think the value of difficulty being passed to submit-tx is a string, when your schema says it should be a set of keywords. you can throw in a print call to make sure, though I think it'll work if you pass the value to keyword
and remove the :set bit from your schema
🙏, yep :set
was the problem, it passes without it, both as a keyword
and a string
.
Have I got the wrong idea about :set
?
I assumed it prevented duplication in the db .eg .."Tetris", :game/difficulty :hard :hard
but perhaps it's only meant to prevent duplication at the transaction level.
At the document level, how might you handle a game update where there could be the potential for duplicated difficulty levels? Or similarly a favorite list where multiple game id's could appear in a vector but no more than once?
Can a single game have multiple difficulties? If so I probably gave you the wrong advice. (I was assuming a :game
document means a specific instance of a game, e.g. "alice and bob are now playing yahtzee", vs. the more general concept of "yahtzee" itself--I'll blame it on skimming your message from my phone earlier 🙂)
If a game can have multiple difficulties, than your schema was right to begin with, and you can instead update the submit-tx
call to wrap the value in a set:
(defn new-game [{:keys [params] :as req}]
(biff/submit-tx req
[{:db/doc-type :game
:xt/id (random-uuid)
:game/name (:name params)
:game/difficulty (set (keyword (:difficulty params)))}])})
If you're updating an existing document and you want to insert another value into a set, you can also use :db/union
-- see https://biffweb.com/docs/reference/transactions/ for an example
Out of curiosity--if a game can have multiple difficulties, is game-form
intended to provide multiple select? It's currently just a single-select, right?
not sure if this helps, but the latest main
from malli can coerce homogenous :enum
values from strings to keywords, symbols and numbers: https://github.com/metosin/malli/pull/782
You had it right the first time, each game should have a single difficulty 😄. Apologies, my follow-up question isn't easy to follow. :game/difficulty should compare the difficulties between the concept of a game not an instance:
.. {:game/name "chess", :game/difficulty :hard}
.. {:game/name "monopoly", :game/difficulty :easy}
Given that, how might I prevent the following update function from passing in multiple difficulties?
E.g.
{.. "chess", :game/difficulty :hard :easy}
Update handler
...
(biff/submit-tx req
[{:db/op :update
:db/doc-type :game
:xt/id (:parse uuid)
:game/difficulty (:difficulty params)}])
...)
Game-update form
[:select
{:name "difficulty"}
[:option {:value :easy} "easy"]
[:option {:value :medium} "medium"]
[:option {:value :hard} "hard"]]
re: coercion--that's handy, I wasn't aware of that!
that update handler will overwrite the difficulty value, so there will only be a single value. the document won't have multiple values unless you explicitly pass in a vector/set/some other type of collection
I wonder if you're thinking about this in an entity-attribute-value centric way, similar to how datomic works? this is an example of how XT's document-centric approach differs--when you submit a document it completely overwrites the entire previous document; it doesn't add individual facts accumulatively. (feel free to ignore if this just confuses things more!)