Fork me on GitHub
#pathom
<
2020-04-01
>
souenzzo13:04:49

Hello pc/reader3 is trying to "inspect" my "entity" values, and it cause issues

(let [register [(pc/resolver
                  `foo
                  {::pc/input  #{:db}
                   ::pc/output [:b]}
                  (constantly {}))]
      parser (p/parser {::p/plugins [(pc/connect-plugin {::pc/register register})]})
      env {::p/reader [p/map-reader
                       pc/reader3
                       pc/open-ident-reader]}
      conn (d/connect (doto (str "datomic:mem://" (d/squuid))
                        d/create-database))
      db (d/db conn)]
  (parser env `[(:b {:pathom/context {:db ~db}})]))
reader3 will throw because "walks" into db and try to get :children from datomic....Datum reader2 work fine

wilkerlucio15:04:21

:pathom/context is only supported in ident joins

souenzzo16:04:53

also occur in ident join I removed the ident join when i'm "trim down" to the "root problem"

souenzzo13:04:43

(let [register [(pc/resolver
                  `foo
                  {::pc/input  #{:db :a}
                   ::pc/output [:b]}
                  (constantly {}))]
      parser (p/parser {::p/plugins [(pc/connect-plugin {::pc/register register})]})
      env {::p/reader [p/map-reader
                       pc/reader3
                       pc/open-ident-reader]}
      conn (d/connect (doto (str "datomic:mem://" (d/squuid))
                        d/create-database))
      db (d/db conn)]
  (parser env `[{([:a "a"] {:pathom/context {:db ~db}})
                 [:b]}]))

wilkerlucio13:04:48

sorry man, I don’t have the time to dig into this now (just looking in this example I don’t have any ideas on what is wrong), too many things happening right now, if you think that’s a bug please make a full example and open an issue on pathom or pathom-datomic (wherever you find its more relevant)

souenzzo13:04:00

but it should work, right?

wilkerlucio13:04:15

well, I don’t see you using the datomic plugin, I dont understand what p/reader3 trying to inspect means, so I have no idea whats going on in this

souenzzo21:04:04

My problem: (map? db) return true with datomic db so functions like p/lift-placeholders that use clojure.walk, walk in datomic db, that can be problematic for performance but in my case, it's problematic because it thows when try to "walk" into Datum for examepl After fix lift-placeholders I found out that p/map->shape-descriptor has the same problem (not by clojure.walk, but by trying to use datomic db as map? ) I have 2 proposals: A: Create a IFinalValue protocol

(defprotocol IValue
  (final-value? [this]))
(extend-protocol IValue
  Object
  (final-value? [this ] false)
  nil
  (final-value? [this ] false))
So the user can extend it to datomic db for exemple to prevent this problem B: Create a ::p/final-value? optinal key inside env that defaults to (constantly false)

lilactown14:04:34

I’m trying to map out how a pathom/eql client would cache results of queries

lilactown14:04:10

it seems like there are two ways to assert whether some data is about the same entity based on a result: • an ident is directly queried for e.g. [{[:customer/id 123] [:customer/name :customer/email]}] the result of which would look like {[:customer/id 123] {:customer/name "Foo" :customer/email "bar"}} • a query results in a record(s) that contain some field that identifies the entity, e.g. the query [::latest-product] that results in {:product/id 1 ,,,}

lilactown14:04:32

any others people can think of?

lilactown14:04:27

the client caching problem seems a little hard to start due to the fact that there’s no explicit schema. I need some way of detecting whether something is an ident/identifying field, or have the user specify out of band. also the fact that it’s ambiguous whether a result will be a collection or scalar makes it harder to wrap my head around right now

lilactown15:04:23

hmm yeah, in GraphQL the result typically has some metadata: a __typename field that denotes whether a part of a result is a queryable object. if it’s not there, then you can reason that the part of the result you’re looking at is just some random JSON, not a part of the graph

lilactown15:04:28

apollo uses the typename and (by default) the id field to construct a cache key

lilactown15:04:13

yeah actually I have no idea how to tell if a part of a result is a queryable thing or just some random EDN.

wilkerlucio15:04:04

@lilactown yeah, in fulcro the idents take care of this problem, because of the open nature of Pathom you need to add something like that to compare identities

wilkerlucio15:04:38

because in pathom you may have multiple identities for the same thing, and also, different identities can partially share some values, but not others

lilactown15:04:24

I’m still trying to grok how idents work in fulcro (and how it’s different than idents in EQL and Pathom)

Björn Ebbinghaus16:04:24

It isn‘t different than eql and pathom.

lilactown15:04:34

it seems like a system would usually have a relatively global notion of identity. e.g. customer-ident and product-ident would be used throughout the app, right?

lilactown15:04:17

fulcro seems to couple identity to a component, which seems weird, but I am probably missing something

wilkerlucio16:04:30

@lilactown fulcro integrates that because of automatic normalization, having that in the component allows fulcro to transform a response tree into a normalized database

wilkerlucio16:04:09

so when new data enters the system, we can use a combination of component ident + props (ident is really just a fn applied to props) to decide where in the DB this data will go

wilkerlucio16:04:28

so components with the same identity end up sharing data in the same place on the db (they get merged there), makes sense?

lilactown16:04:06

I think so 😅

lilactown16:04:46

I’d like to be able to have a library that works like:

(def all-customers-query
  [{:customers/all [:customer/id :customer/name :customer/email]}])

;; using helix for React components
(defnc customer-list
  []
  ;; read from the cache all of the customers, maybe go fetch them
  (let [customers (use-query all-customers-query)]
   ,,,))
and then elsewhere:
(def customer-detail-query
  '[{[:customer/id ?id] [:customer/name :customer/age :customer/email :customer/phone ,,,]})

(defnc customer-detail
  [{:keys [customer/id]}]
  ;; read from the cache this specific customer, maybe go fetch it
  (let [customers (use-query customer-detail-query {:id id})]
    ,,,))
and it sounds like in order for these two components to maintain data consistency, I need to also add an additional mechanism that takes the result and computes an ident for each of the queries. something like
(def customer-detail-query
  '[{[:customer/id ?id] [:customer/name :customer/age :customer/email :customer/phone ,,,]}))

(defn customer-detail->ident
  [detail]
  ;; result will be {[:customer/id ?id] ,,,}
  (first (keys detail)))

(def all-customers-query
  [{:customers/all [:customer/id :customer/name :customer/email]}])

(defn all-customers->idents
  [all-customers]
  ;; result will be [{:customer/id ?id ,,,} ,,,]
  (map :customer/id all-customers))

lilactown16:04:08

sorry for the wall of text.

lilactown16:04:11

which I guess makes sense… but seems tedious

souenzzo16:04:53

I don't think that this ?id template thing is a good path to solution The point of sale of EQL is that it's clojure data, so you don't need "query parameters" or "templates", you can just "assoc/concat" things

(def customer-detail-query
  [:customer/name :customer/age :customer/email :customer/phone])
(defnc customer-detail
       [{:keys [customer/id]}]
       ;; read from the cache this specific customer, maybe go fetch it
       (let [customers (use-query [:customer/id id] customer-detail-query)]))
Then use-query wil build/compose the final query

lilactown16:04:51

fair, it’s all just ideation

souenzzo16:04:09

As #fulcro, you can add metadata in queries

(def product-query
  ^{:ident :product/id} [:product/id,,,])
(def customer-query
  ^{:ident :customer/id} [:customer/id,,,
                          {:customer/products product-query}])

souenzzo16:04:45

But I think other solutions also may be possible: - server response with normalization metadata - global "index key" registry - Every map should contain a :index-by :costumer/id key

souenzzo16:04:04

oops, reponses in wrong thread. but ok.

lilactown16:04:17

haha 🙂 I appreciate the responses!

souenzzo16:04:05

in fulcro case, you declare with with render so

(defsc Foo [this prop]
  {:query [:foo/id :foo/name]
  :ident :foo/id}
  (div "Ok"))
(defsc Bar [this prop]
  {:query [:bar/id {:bar/foo (get-query Foo)}]
  :ident :bar/id}
  (div "Ok"))
All query/metadata is handled my defsc and get-query

lilactown16:04:32

how does fulcro handle multiple idents in a single query?

lilactown16:04:00

or is that even a thing

souenzzo16:04:09

You can normalize the same data (users for example) both with user/id and user/email, for example I think that it's a anti-pattern. Not sure. This decision is on the component

lilactown17:04:40

hahahaha fair 😂

souenzzo17:04:34

"just functions"

lilactown16:04:52

that sort of dovetails into something I’ve been wondering about: how does fulcro handle a query with multiple idents? e.g. I need the customer and the product they most recently bought:

[{[:customer/id 123]
  [:customer/name
   :customer/email
   {:customer/last-purchase
    [:product/id :product/name :product/price]}]}]
ostensibly there’s two identities here: :product/id and :customer/id