Fork me on GitHub
#pathom
<
2021-07-26
>
cyppan09:07:00

It would be nice to be able to rename fields in the EQL queries (like aliases in GraphQL)

souenzzo11:07:56

@cyppan in pathom (2) there is an almost undocumented feature: [(:a {:pathom/as :b})] In pathom3 we still not have, as far I know

👍 3
souenzzo11:07:02

but it should be possible to implement in pathom3 as a plugin

cyppan11:07:51

good idea, I’ll try that

souenzzo11:07:42

@cyppan I think that is nice to have an "standalone" eql-select library, where you can simply call (select {:a 42} [(:a {:as :b})]) => {:b 42} Then develop a plugin to use eql-select as a pathom3 plugin

cyppan11:07:29

The Pathom2 way makes more sense to me

🆗 3
cyppan11:07:59

Just to rename some fields on output

souenzzo11:07:47

if anyone is interested in publishing an eql-select library here a "base implementation" from my codebase 🙂 Maybe handle :default too on the library https://gist.github.com/souenzzo/cb37af999d2ab76e68e465427f1f3cd9

🙏 3
wilkerlucio14:07:20

I'm trying to discourage this at this moment, but you can make an plugin using this extension point: https://pathom3.wsscode.com/docs/plugins#pcrwrap-merge-attribute

wilkerlucio14:07:08

you can read the params with (-> env ::pcp/node ::pcp/params), you can use that to re-implement something like :pathom/as

cyppan15:07:09

As a first very naive attempt, I used the wrap-map-select-entry extension point, following the protect-attributes-wrapper example

; define the extension wrapper fn
(defn rename-output-wrapper [mse]
  (fn [env source {:keys [key] :as ast}]
    (if-let [as (get-in ast [:params ::p.eql/as])]
      (coll/make-map-entry as (get source key))
      (mse env source ast))))

(p.plugin/defplugin rename-output-plugin
  {::pf.eql/wrap-map-select-entry rename-output-wrapper})

(comment
  (p.eql/process env {:todo/id 1} ['(:todo/name {::p.eql/as :todo/overriden-name})])
  )

wilkerlucio16:07:35

looks good, sorry I gave a bad instruction about how to get the params before, but you got it right 👍

👍 2
wilkerlucio16:07:05

but there may be a problem there, I'm checking now, because going this way will prevent the children from use the same algorithm (it will not filter things that has :as)

wilkerlucio16:07:44

yeah, confirmed the issue:

(p.eql/process
  (-> (pci/register [entity all-entities])
      (p.plugin/register rename-output-plugin))
  [{'(:all {::p.eql/as :foo})
    [:name
     ]}])
=> {:foo [{:id 1, :name "a", :address {:n 100}} {:id 2, :name "b"}]}

wilkerlucio16:07:52

(not asked for address, but ended up out there)

wilkerlucio16:07:13

fixed version:

(defn rename-output-wrapper [mse]
  (fn [env source ast]
    (let [entry (mse env source ast)]
      (if-let [as (get-in ast [:params ::p.eql/as])]
        (coll/make-map-entry as (val entry))
        entry))))

cyppan16:07:21

indeed better thanks !

cyppan16:07:25

And for the why I need that, our front is in ReScript (so not very EQL-friendly), so I’m building a GraphQL server wrapper (using Lacinia) on top of Pathom resolvers on our backend API, and GraphQL supports that (to alias fields)

souenzzo16:07:17

@cyppan if you are using lacinia, I think that it will do the alias for you, you don't need to worrry about that in pathom side.

cyppan16:07:33

just tried, you’re right 👍

bmo 2
souenzzo16:07:12

I did an (abandoned) prototype once Waiting to see your solution 🙂

wilkerlucio17:07:21

this is indeed something that I would love to see too, there is also a discussion for that: https://github.com/wilkerlucio/pathom3/discussions/18

cyppan06:07:33

Yeah not that easy I guess 🙂 I’ll add a note on the discussion

cyppan12:07:23

On the last alpha version (2021.07.23-alpha) I can see the pathom3 strict mode does not work for batch resolvers

wilkerlucio14:07:40

this seems a bug, I can look at that today, thanks for the report

👍 2
wilkerlucio14:07:45

but I also wonder, why are you disabling cache on this resolver? cache is kinda important for Batch, because without reliying on the previous input to cache, its hard to know if that part was tried already.

cyppan14:07:17

I guess I don’t really understand what is cached, if it is the resolver output, I don’t want caching because I often want the most recent data from the DB

wilkerlucio15:07:46

by default the cache lives only during a single request

wilkerlucio15:07:00

its to avoid re-doing the same operation from different parts of the same query

cyppan15:07:51

Ah! I missed that in the doc thank you > without reliying on the previous input to cache, its hard to know if that part was tried already. I don’t really understand that, I’m gonna have a look into the code

wilkerlucio15:07:39

I gonna fix it anyway, because its a bug, it shouldn't rely only on that

wilkerlucio15:07:03

but what happens, specially in the case of placeholders, is that you ahve to process the same stuff in different parts of the tree

wilkerlucio15:07:39

since Pathom concept of entity is quite flexible, the only way to be sure we are trying to same thing is if the resolver gets called with the same exact input, from a different place

wilkerlucio15:07:31

this when cache kicks in, also in the base of batch, the items are cached as if they were individual calls, so when we see that same data need in another part of the query, it can avoid re-calling the resolver by using batch

cyppan15:07:25

I think I got it, thank you

cyppan12:07:56

And I’m not sure how I should handle resources that do not exist in the database in my resolvers to avoid this :thinking_face:, throw an “not-found” exception? It feels strange to me that the processor goes into an infinite loop (in “lenient” mode) when it can’t get the expected output, what I would expect actually is a way to make the ::pco/output nilable, a bit like optional inputs (pco/? ...)

wilkerlucio14:07:04

Pathom 3 is still alpha, some things are still getting figured out, and batch is one of then, there's been some issues with the current way to process it, so its good to collect these kinds of issues so we can figure how to make a better implementation

wilkerlucio14:07:43

specially with the very recent addition of strict mode, things are getting worked out, can you bring the specific case you have so we can see what we can do about it?

cyppan15:07:39

No problem with the alpha state, I’m happy to share and contribute in any way possible, Pathom is a really nice abstraction 🙏

cyppan15:07:17

Here I think my concern is not really the batch resolver: My backend is MongoDB so I have documents sometimes with keys sometimes without, I use those fetched documents directly in my Pathom resolvers, so it happens that a pco/output key is not present in the response. For instance, an account-resolver , for a :account/id input, declares the following outputs: :account/name and :account/avatar but avatars are optional, without even the avatar key sometimes in my Mongo documents, and this makes Pathom go into an infinite loop when I request :account/avatar in my EQL.

cyppan15:07:51

I can merge a default map with all pco outputs keys with nil associated, but I wonder if there is something more elegant maybe to do

cyppan15:07:59

like an optional ::pco/output, but I’m not sure of the impacts on the execution plan

cyppan15:07:51

It’s more like a “default” for a ::pco/output maybe, but again, hard to integrate into the graph path resolution I guess

wilkerlucio15:07:40

its ok to have optional things on the output, you can look at the output more as like: "I may provide these attributes"

wilkerlucio15:07:30

the strictiness is always at the consumer side, so its not about the provider to say "I will for sure provide this", instead, its up to consumer to say "I *require* this part, while these other part may be optional"

cyppan15:07:26

Since there is no concept of “schema”, I guess the information of what keys are optional or not has to be expressed another way, so yes it makes sense

cyppan15:07:46

giving that responsibility to the consumer

wilkerlucio15:07:28

yeah, and I think this a good place to be, in the same way we want the consumer to decide what he wants in the query, he is also responsible for deciding what is optional about it

👍 2
wilkerlucio15:07:56

and to point out, when I say consumers I'm refering to resolver inputs and queries sent to p.eql/process

👍 2
cyppan15:07:40

(I was thinking of several resolvers* providing the same outputs, and how to choose / fallback to another path when you have required and optional outputs, wondering how to handle that)

cyppan15:07:37

when you say “its ok to have optional things on the output” you mean, as a resolver, I’m not forced to provide you the keys I have declared?

wilkerlucio15:07:49

on multiple options Pathom will generate OR nodes in the plan, so it will try one at a time, there is a built-in thing you can use to prioritize, but this is still an area of exploration, not many people have used it so not enough experience, but there is a discussion about it here: https://github.com/wilkerlucio/pathom3/discussions/57

👀 2
wilkerlucio15:07:01

> when you say “its ok to have optional things on the output” you mean, as a resolver, I’m not forced to provide you the keys I have declared? correct

cyppan15:07:30

Ok, when the bug I’ve pointed out is solved it should work directly cool

wilkerlucio15:07:31

Pathom never complains about missing data on the output, it complains if the the data is missing by the time it tries to call something that requires it

wilkerlucio15:07:39

here is an example that illustrates that:

(pco/defresolver ab []
  {::pco/output
   [:a :b]}
  {:a 1})

(pco/defresolver c [{:keys [a]}]
  {:c (inc a)})

(p.eql/process-one
  (pci/register [ab c])
  :c)

cyppan15:07:57

Yes I understand, this is problematic in my case because I have “foreign keys” in my mongo documents, but sometimes without the key as I was explaining, so when I try to “join” and there isn’t the key, it errors, I’m merging nil keys to solve that

wilkerlucio15:07:02

ab says it outputs :b, but it doesn't, but since :c doesn't care about :b. not an issue

👍 2
wilkerlucio15:07:42

its valid to make join keys optional, is this something that works for your case?

wilkerlucio15:07:56

like: [:foo {(pco/? :join-bar) [:baz]}]

cyppan15:07:51

Oh! what is this, a resolver output ?

wilkerlucio15:07:44

no, on the input (or query)

wilkerlucio15:07:54

resolver outputs never need optional mark

wilkerlucio15:07:27

can you show the place where you demand the join? I know its a bit weird at first this thing about requirements on the consumer

cyppan15:07:07

Sorry, not sure I follow you

cyppan15:07:08

in my case I do something like that, [:meta-campaign/brand {(pco/? :brand_expanded) [,,,]]

cyppan15:07:52

the join key is :meta-campaign/brand, which sometimes is not present from my meta-campaign resolver output

cyppan15:07:43

If I understand correctly it should work with [:meta-campaign/brand {(pco/? :brand_expanded) [,,,]] but not with [:meta-campaign/brand {:brand_expanded [,,,]]

wilkerlucio15:07:11

let me coin an example of what I have in mind, so we can go around it

👍 2
wilkerlucio15:07:23

(def entity-db
  {1 {:name    "a"
      ; has address
      :address {:n 100}}
   2 {:name "b"
      ; no address
      }})

(pco/defresolver entity [{:keys [id]}]
  {::pco/output
   [:name {:address [:n]}]}
  (get entity-db id))

(pco/defresolver all-entities []
  ;just output the ids
  {:all
   (mapv #(array-map :id %) (keys entity-db))})

(p.eql/process
  (pci/register [entity all-entities])
  [{:all [:name {(pco/? :address) [:n]}]}])

wilkerlucio15:07:43

note the optionality on the query side, and this works the same when making ::pco/input

cyppan15:07:17

I see, I understand better what you meant by “requirements on the consumer side”

cyppan15:07:22

it would be great to have this example in the doc

cyppan15:07:57

thank you for your time 🙂

wilkerlucio15:07:18

no worries, glad we got thought it

wilkerlucio15:07:26

if you find a good place to add this example, PR is welcome 🙂

👍 2
wilkerlucio15:07:42

maybe on the optional input section

cyppan15:07:13

yep was thinking about there

souenzzo12:07:52

it seems a pathom3 bug