Fork me on GitHub
#pathom
<
2019-04-17
>
Ahmed Hassan05:04:40

I want to communicate with Postgresql for fetching and posting data from Pathom in Fulcro application, are there any examples and code for help to get a started? I'm using Honeysql for queries. (Additional question) What migration library do you use for SQL DBs?

hjrnunes13:04:32

Hello, I'm trying to get this example going, but I'm puzzled by the response. Why am I getting channels out? Can anyone shed any light on this? Thanks! Query:

(parser {} [{:t/data [:t/id :t/text :t/test_train :t/label]}])
Response:
#:t{:data #:com.wsscode.pathom.parser{:provides #{:t/data},
                                      :response-stream #object[clojure.core.async.impl.channels.ManyToManyChannel
                                                               0x70c6abd9
                                                               "clojure.core.async.impl.channels.ManyToManyChannel@70c6abd9"]}}

Parser impl:
(def data [{:t/id         1
            :t/text       "asadad"
            :t/test_train "train"
            :t/label      "a"}
           {:t/id         2
            :t/text       "assdgsd vvdad"
            :t/test_train "train"
            :t/label      "b"}
           {:t/id         3
            :t/text       "asadadw kjonjo"
            :t/test_train "test"
            :t/label      "c"}])

(defn mk-resolver []
  {::pc/sym     (symbol "my-ns" "my-resolver")
   ::pc/output  [{:t/data [:t/id :t/text :t/test_train :t/label]}]
   ::pc/resolve (fn [_ _] {:t/data data})
   })

(defn mk-index []
  (-> {} (pc/register (mk-resolver))))

(def parser
  (p/parser
    {::p/env     {::p/reader               [p/map-reader
                                            pc/parallel-reader
                                            pc/open-ident-reader
                                            p/env-placeholder-reader]
                  ::p/placeholder-prefixes #{">"}}
     ::p/plugins [(pc/connect-plugin {::pc/indexes (atom (mk-index))})
                  p/error-handler-plugin
                  p/request-cache-plugin
                  p/trace-plugin]}))

hjrnunes13:04:22

I'm using the map version of the resolver because I intend to generate them dynamically later on. Is this even a good idea at all, btw?

wilkerlucio14:04:21

@hjrnunes hello 🙂 the issue in your code is that you are using a serial parser combined with the pc/parallel-reader, those are not compatible, pc/parallel-reader requires the parallel-parser, if you just want serial parser you can replace the pc/parallel-reader with pc/reader2

hjrnunes14:04:27

@wilkerlucio Ah, I see! Makes sense

hjrnunes14:04:20

Is there a big advantage in going parallel for simple stuff?

wilkerlucio14:04:51

the parallel (as the name says) can run things in parallel, it really depends on how your data is distribuited

wilkerlucio14:04:33

some system will be more parallelizable than others, as you grow it usually gets good things, but in some cases the serial can be faster (when there is no room for parallelism)

wilkerlucio14:04:07

also, the mk-resolver helper you created, there is one in pathom already, pc/resolver or pc/defresolver

hjrnunes14:04:34

Does it allow me to register any symbol?

wilkerlucio14:04:57

yup, whatever you send on the map will be the one it picks

wilkerlucio14:04:19

but keep in mind that resolver names doesn't matter much, they just need to don't collide with others

wilkerlucio14:04:40

you rarely reference resolvers by symbol

hjrnunes14:04:45

So is there any problem in creating resolvers dynamically at all?

wilkerlucio14:04:02

not at all, the graphql integration does a lot of that

hjrnunes14:04:14

I'm thinking about using EQL for a very simple data language. I don't actually need the tree aspect of the data, but I could use the expressiveness of the query parameters. Something like a poor man's Walkable, but without all the SQL bells and whistles, and without joins

hjrnunes14:04:31

Like to query one single table

hjrnunes14:04:03

However, I do need global query params, so to speak

hjrnunes14:04:58

Walkable does the :table/all join to get a bunch of rows, and I can apply :limit to that join

hjrnunes14:04:12

is that the best way?

hjrnunes14:04:03

Probably wasn't very clear. EQL params have to be applied to either a property, ident or join, right?

hjrnunes14:04:22

there's no way to apply them to the whole query itself?

hjrnunes14:04:01

Also, in the resolver, how do I get the parameters? Is it supposed to be from the query in env?

wilkerlucio14:04:22

@hjrnunes yes, you get from env (-> env :ast :params), about the globally aplication, that can be done with plugins, you can write a plugin that captures the params on read and make it part of env, doing that will propagate that inforamtion to every subchildren, still you can't do at query level, but can be at something that queries everything else

wilkerlucio14:04:30

to do query level you can use query meta-data if you want

wilkerlucio14:04:36

that's a bit different but works as well

hjrnunes15:04:56

hmm I see. An explicit join is probably clearer. Thanks!

hjrnunes15:04:52

is (-> env :ast) supposed to contain the portion of the AST relevant for the resolver, or do I get-in with the path?

hjrnunes15:04:41

@wilkerlucio Thanks for your help and work on Pathom and Walkable. All in all, really useful and impressive work!

wilkerlucio15:04:18

thanks, but I can't take the one on Walkable, that one you need to thank @U0E2YV1UZ 🙂

hjrnunes15:04:24

@wilkerlucio can I manipulate ::pc/processing-sequence to filter out entities? Basically, I want to apply a limit to the results per property value. [{:ents [:e/prop1 e:/prop2 (e/prop3 {:limit 2})]}] would return at most 2 rows for each distinct value of :e/prop3. I'm aware this can be done at the join level, but I like the expressiveness of doing it like this, because I'll be having a typical limit at the join level too.

wilkerlucio15:04:22

messing with processing-sequence is a bad idea, thats an internal thing

wilkerlucio15:04:42

maybe what you want can be better served as a plugin

wilkerlucio15:04:59

so you can parse params and limit results across the whole parsing

hjrnunes16:04:57

yeah, so i'd collect any :limit params at the property level, stick them on the env, and then filter out when resolving :ents?

wilkerlucio16:04:49

@hjrnunes hum, I would be careful about using limit on env, sounds weird that a limit in a parent would affect some joins many levels down, usually that is a thing per join

wilkerlucio16:04:15

you still can do it, but you would be complecting parts of the query in a way that's not obvious

wilkerlucio16:04:44

with a plugin you can implement the limit once and it will work anywhere the user adds the param (in joins returning lists)

wilkerlucio16:04:49

thats a quite easy plugin to write

wilkerlucio16:04:42

@hjrnunes

(def limit-plugin
  {::p/wrap-read
   (fn [reader]
     (fn [env]
       (if-let [limit (-> env :ast :params ::limit)] ; use qualified keyword so it doesn't clash with other people things
         ; let-chan only required to support async/parallel parsers
         (let-chan [res (reader env)]
           (if (sequential? res)
             (vec (take limit res))
             res))
         (reader env))))})

hjrnunes16:04:37

@wilkerlucio I'll give it a go! Thanks again!

mitchelkuijpers18:04:10

I was wondering something about pathom, since you can use placeholders in queries. Do people actually use it instead of the default fulcro parser?

wilkerlucio18:04:13

@mitchelkuijpers I did some tests around it, the problem is that pathom is too slow to work as the client parser, client parser needs to be really fast (and it tries to optimize on that, for example, it doens't even convert the query to AST, parse the query directly to reduce overhead), pathom on the other side is designed for flexibility, so a lot of wrappers and injection points that add overhead

mitchelkuijpers18:04:58

Ah that clears it up, then I wont waste any time on it. I was just curious

wilkerlucio18:04:29

@mitchelkuijpers but if you need for other purposes (maybe want to simulate fulcro reader on the server), pathom has that setup ready too: https://github.com/wilkerlucio/pathom/blob/master/src/com/wsscode/pathom/map_db.cljc

mitchelkuijpers18:04:34

Ah nice, No was just wondering since we sometimes use placeholder queries with pathom. Since it makes structuring some UI stuff a bit easier

wilkerlucio18:04:01

yeah, the good part is that the client parser doesn't need to know about that, in the end it turns in regular fulcro links 🙂

mitchelkuijpers18:04:31

Yeah doesn't really matter for Fulcro luckily

mitchelkuijpers18:04:33

Btw I tried out the index browser today but it seems to fail on:

::pc/transform pc/transform-batch-resolver

mitchelkuijpers18:04:48

With this error:

mitchelkuijpers18:04:55

[INFO] [talledLocalContainer] Caused by: java.lang.Exception: Not supported: class com.wsscode.pathom.connect$batch_resolver$fn__36671                                                               
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:194) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.JsonEmitter.emitMap(JsonEmitter.java:171) [?:?]                                                                                        
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitMap(AbstractEmitter.java:85) [?:?]                                                                                 
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:184) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.JsonEmitter.emitMap(JsonEmitter.java:171) [?:?]                                                                                        
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitMap(AbstractEmitter.java:85) [?:?]                                                                                 
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:184) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.JsonEmitter.emitMap(JsonEmitter.java:171) [?:?]                                                                                        
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitMap(AbstractEmitter.java:85) [?:?]                                                                                 
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:184) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.JsonEmitter.emitMap(JsonEmitter.java:171) [?:?]                                                                                        
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitMap(AbstractEmitter.java:85) [?:?]                                                                                 
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:184) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitArray(AbstractEmitter.java:97) [?:?]                                                                               
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.marshal(AbstractEmitter.java:182) [?:?]                                                                                
[INFO] [talledLocalContainer]   at com.cognitect.transit.impl.AbstractEmitter.emitTagged(AbstractEmitter.java:49) [?:?]                                                                             

mitchelkuijpers18:04:22

But now that I am reading it is probably because it tries to convert a fn to transit