Fork me on GitHub
#datomic
<
2015-11-28
>
genRaiy17:11:59

guys … a quick design question for components in Datomic...

genRaiy17:11:46

I am trying to model a shopping basket with some items in the basket and figured that the items should be a component of the basket

genRaiy17:11:03

it all worked out nicely but ...

genRaiy17:11:00

I noticed that when I add another element to the basket that the items are duplicated

val_waeselynck17:11:03

@raymcdermott: so that's a bug right ?

genRaiy17:11:06

or maybe to put another way… that I’m finding it unintuitive to model the current state of the items in the basket using the component model

genRaiy17:11:20

I’m not sure...

genRaiy17:11:42

which is why I asked

genRaiy17:11:50

maybe I’m doing it wrong

val_waeselynck17:11:14

@raymcdermott: I'm not sure I understand what you get

val_waeselynck17:11:22

could you put some code somewhere maybe ?

genRaiy17:11:23

this is my original cart

genRaiy17:11:35

(def cart [{:db/id     #db/id [:db.part/user -1]
            :cart/id   #uuid "d213198b-36b5-4c19-8cb1-e172f59091d9"
            :cart/name "My Shopping Cart"
            :cart/sku-counts
                       [
                        {:sku-count/sku    12345
                         :sku-count/count  1}
                        {:sku-count/sku    54321
                         :sku-count/count  2}
                        ]
            }
           ])

genRaiy17:11:44

and then an updated cart

genRaiy17:11:02

(def new-cart [{
                :db/id 17592186045421
                :cart/sku-counts
                       [
                        {:sku-count/sku   12345
                         :sku-count/count 1}
                        {:sku-count/sku   54321
                         :sku-count/count 2}
                        ]
                }
               ])

genRaiy17:11:35

and it creates two new records even though the data has not changed

val_waeselynck17:11:59

is :sku-count/sku an identity field ?

genRaiy17:11:27

{:db/id                 #db/id[:db.part/db]
  :db/ident              :sku-count/sku
  :db/valueType          :db.type/long
  :db/cardinality        :db.cardinality/one
  :db/doc                "Number of the SKU"
  :db.install/_attribute :db.part/db}

val_waeselynck17:11:58

It's normal that it gets duplicated then. 2 strategies here :

genRaiy17:11:10

{:db/id                 #db/id[:db.part/db]
  :db/ident              :cart/sku-counts
  :db/isComponent        true
  :db/valueType          :db.type/ref
  :db/cardinality        :db.cardinality/many
  :db/doc                "SKUs with counts for this cart"
  :db.install/_attribute :db.part/db}

val_waeselynck17:11:38

1) have an identity field on the cart items, and do insert or update based on whether the line item is created or updated

val_waeselynck17:11:07

2) On each transaction, erase the past line items and replace with the updated ones (it's a 'document-like' approach)

genRaiy17:11:31

ok, so in either case I have to take care of the state rather than (my naive understanding) that it would be handled by Datomic because from my view there is no novelty

val_waeselynck17:11:37

For the second approach, I ended up writing a database function that does this:

genRaiy17:11:03

thanks - so I guess it’s a common issue!

genRaiy17:11:33

and how did you insert the snippet (not a Slackista)

val_waeselynck17:11:57

click the + sign in the bar at the bottom of the screen

val_waeselynck17:11:21

And it's not that common - don't be too eager to use something like this simple_smile

genRaiy17:11:53

I take it you mean the function

genRaiy17:11:09

isn’t this a classic master / detail

genRaiy17:11:03

I mean the need to update component entries

genRaiy17:11:39

basket / items ; order / order-lines ; etc...

val_waeselynck17:11:48

Most of the time I would add /update /delete the line items individually, instead of 'resetting' the cart all the time

genRaiy17:11:37

ok, so just so I understand … why didn’t you do that?

genRaiy17:11:57

i would like to understand the trade-offs

val_waeselynck17:11:31

I didn't have a choice, I was migrating from a Document database, and my legacy clientside code relied on this design

genRaiy17:11:29

ah, ok. I think the add / update / delete approach is simpler for this use case

genRaiy17:11:58

@val_waeselynck: thanks for the suggestions

val_waeselynck17:11:04

it really depends on what granularity is permitted on the client

val_waeselynck17:11:22

One thing to consider is that transaction functions are more costly

genRaiy17:11:54

it’s just a toy at the moment

genRaiy17:11:00

so I can decide

genRaiy17:11:31

I think it’s going to be easier to update the existing counts

genRaiy17:11:02

the nested map is probably ideal for a case where the data will not change or only rarely

genRaiy17:11:18

I guess another benefit is that deleting the outer item will delete the components so that’s less tedious

genRaiy17:11:39

anyway, I’ll get back to my toys - thanks

genRaiy21:11:39

following on from the earlier conversation … real life crept in...

genRaiy21:11:54

anyway I now have this situation...

genRaiy21:11:58

(clojure.pprint/pprint (d/pull db '[*] cart-id))
{:db/id 17592186045421,
 :cart/id #uuid "d213198b-36b5-4c19-8cb1-e172f59091d9",
 :cart/name "My Shopping Cart",
 :cart/sku-counts
 [{:db/id 17592186045422, :sku-count/sku 12345, :sku-count/count 1}
  {:db/id 17592186045423, :sku-count/sku 54321, :sku-count/count 2}
  {:db/id 17592186045425, :sku-count/sku 12345, :sku-count/count 1}
  {:db/id 17592186045426, :sku-count/sku 54321, :sku-count/count 2}]}

genRaiy21:11:26

and I would like to retract the first two sku-counts

genRaiy21:11:54

but I am struggling to locate the right way to achieve the retraction … docs are not feeling obvious

genRaiy21:11:01

This is close (I think) but I get errors...

genRaiy21:11:29

(def retraction [:db/retract 17592186045422 :sku-count/sku 12345 :sku-count/count 1])
=> #'shopping-cart-demo.datomic/retraction
@(d/transact conn retraction)
IllegalArgumentExceptionInfo :db.error/not-transaction-data Transaction data element must be a List or Map, got :db/retract  datomic.error/arg (error.clj:57)

genRaiy21:11:27

or maybe I’m way off… either way I would appreciate a pointer

genRaiy21:11:09

ok, I have got as far as

genRaiy21:11:27

@(d/transact conn [[:db.fn/retractEntity 17592186045422 ]])

val_waeselynck21:11:08

@raymcdermott: that's the one you want

genRaiy21:11:33

although the pull API still reports the same data

val_waeselynck21:11:34

@raymcdermott: try to always picture the low-level set of datoms that is involved

genRaiy21:11:41

do I need to touch?

val_waeselynck21:11:59

I think you're querying on an old version of your db

genRaiy21:11:10

thanks .. I’m getting there