untangled

NEW CHANNEL: #fulcro
curtosis 2016-07-07T00:44:14.001756Z

so I think I may finally be sort of grokking this... 1) my (server) call gets data into app-state at :sources 2) the reconciler normalizes and stuffs the real data into :sources/by-id and replaces the entries in :sources with ids - this works because I've defined Source as a component that has initial state and query 3) I run a :post-mutation to move the :sources list to [:sources-tab 1 :sources] 4) Profit?

tony.kay 2016-07-07T00:58:00.001757Z

Yep, that's the general idea

curtosis 2016-07-07T00:58:35.001758Z

slowly, emerging from the haze

tony.kay 2016-07-07T00:58:48.001759Z

@curtosis: In plain Om, the general idea is that you merge it in and use a parser to morph it to a different form on the fly. In Untangled, we've found it easier to deal with it this way

tony.kay 2016-07-07T00:59:30.001760Z

everybody understands data transforms and moves. It denormalizes your database a bit, but that is tractable to manage.

tony.kay 2016-07-07T00:59:46.001761Z

well, it CAN denormalize it...

curtosis 2016-07-07T01:00:18.001762Z

heh

tony.kay 2016-07-07T01:01:40.001763Z

for performance of your UI, I kinda felt like you were either going to be doing memoization (and managing the caching of that) in the parser or denormalizing the database. Given that cache invalidation is one of the "hard problems" of CS...a little denormazliation seems pretty tolerable.

curtosis 2016-07-07T01:02:40.001764Z

now the tricky bit is that the Source component itself doesn't actually render ... it's a placeholder for the query, ident, etc. It actually gets rendered by a table component, so I need to deref it (`db->tree`, I think) to get access to the actual data for the cells.

curtosis 2016-07-07T01:02:53.001765Z

yeah, that sounds eminently reasonable.

tony.kay 2016-07-07T01:03:15.001766Z

you mean the thing you did the query with wasn't part of your UI?

tony.kay 2016-07-07T01:03:47.001767Z

You should not need to make the tree. Untangled already has db->tree internally to make the tree. You need to make a subgraph in your db that matches your UI query.

curtosis 2016-07-07T01:04:10.001768Z

so it kind of is in the UI...

tony.kay 2016-07-07T01:04:45.001769Z

Right. Typically you have some sub-portion of the UI that represents real persisted data. And then there is all the local crap that makes up the other bits of UI.

curtosis 2016-07-07T01:04:53.001770Z

I have a Sources component (is a tab) that has a :sources prop that gets its query from a Source component.

tony.kay 2016-07-07T01:05:32.001771Z

Right, and that query needs to compose up the tree to root. If you use InitialAppState to build state up the tree

curtosis 2016-07-07T01:05:36.001772Z

Sources renders a table, but because of the underlying component (fixed-data-table) I don't render it a Source at a time.

curtosis 2016-07-07T01:05:45.001773Z

Yup, that bit's working great.

tony.kay 2016-07-07T01:06:22.001774Z

oh, so you're saying your getting a subgraph from your query, because your query doesn't walk (query for) the entire tree of data?

curtosis 2016-07-07T01:08:05.001776Z

Sources has a list :sources [[:sources/by-id 1] [:sources/by-id 2] ... ] ... but I have to provide a cell function that essentially maps (id, column-key) -> renderable Cell

tony.kay 2016-07-07T01:08:26.001777Z

Each of those cells should be a component that queries for data

curtosis 2016-07-07T01:08:39.001778Z

ahaaaaaaaa!

tony.kay 2016-07-07T01:09:00.001779Z

just keep composing it until you've got components that mirror the data all the way down

curtosis 2016-07-07T01:09:39.001780Z

hmm... would you define a different Cell component per column type? e.g. NameCell, FilenameCell, etc?

tony.kay 2016-07-07T01:10:03.001781Z

Do the different cells need different queries?

tony.kay 2016-07-07T01:10:40.001782Z

If so, that is a union query, just like in tabs, but where the cell type varies over the collection.

tony.kay 2016-07-07T01:11:04.001783Z

So you'd have a "union switcher" component, and yes, a component for each cell type

curtosis 2016-07-07T01:11:21.001784Z

I think yes... NameCell just needs :name; FilenameCell just needs :filename

tony.kay 2016-07-07T01:11:22.001785Z

and your data would need a way to discern which it was in the ident function

tony.kay 2016-07-07T01:11:58.001786Z

It's just like the tabs example, but instead of a single ident you have a vector of them (for each row)

tony.kay 2016-07-07T01:12:12.001787Z

(Row is probably a component too)

curtosis 2016-07-07T01:12:26.001788Z

it is, but I don't get to create the row

tony.kay 2016-07-07T01:14:14.001789Z

That may be part of your data transform: make it look the way you need it to look for the UI

tony.kay 2016-07-07T01:14:38.001790Z

Detect that "if it has :name, I'll add in a :type :namecell", etc

tony.kay 2016-07-07T01:15:39.001792Z

The "magic" has to happen somewhere. In Untangled: it usually happens in a mutation (after a load or event)

tony.kay 2016-07-07T01:15:52.001793Z

Try to limit the magic in the UI layer

curtosis 2016-07-07T01:16:36.001794Z

yeah, that part makes sense. I'm just trying to adapt it to how this 3rd-party component expects things.

tony.kay 2016-07-07T01:16:48.001795Z

oh...you're trying to use a React table component?

curtosis 2016-07-07T01:16:51.001796Z

yep

curtosis 2016-07-07T01:17:04.001797Z

it made more sense than fighting jquery at the time.... 😉

tony.kay 2016-07-07T01:17:32.001798Z

Ah, then what you want to do is this: Don't write query stuff for the parts that go to that component at all. Just drop a blob of JS data there

tony.kay 2016-07-07T01:17:45.001799Z

query for the blob of js data, and pass that through

curtosis 2016-07-07T01:18:20.001800Z

no value in keeping it in the database for other UI bits to operate on?

tony.kay 2016-07-07T01:18:29.001801Z

a "property" in Om/Untangled can have any value: js Date, js map, Datascript database, whatever

tony.kay 2016-07-07T01:18:42.001802Z

it still goes in the app database

curtosis 2016-07-07T01:19:07.001803Z

but not normalized/by-id-ified?

tony.kay 2016-07-07T01:19:24.001804Z

depends

curtosis 2016-07-07T01:19:37.001805Z

heh.. yeah, that was my conclusion too. 😛

tony.kay 2016-07-07T01:19:38.001806Z

If your goal is to display static data from a server: who cares

tony.kay 2016-07-07T01:20:04.001807Z

If your goal is to interact dynamically with the data, then that is more complicated

curtosis 2016-07-07T01:20:11.001808Z

in my case, the table display is just a summary. the real point is to edit the data.

tony.kay 2016-07-07T01:20:40.001809Z

In that case you're probably going to find a parity mismatch with the design of the pre-built component and Om/Untangled.

curtosis 2016-07-07T01:21:03.001810Z

(which I fear leads me down the nested tabs route, but that's a problem for tomorrow...)

tony.kay 2016-07-07T01:21:12.001811Z

If your editing is outside of the table widget, then it isn't too bad

curtosis 2016-07-07T01:21:19.001812Z

yes, that's my plan.

tony.kay 2016-07-07T01:21:21.001813Z

ok

tony.kay 2016-07-07T01:21:25.001814Z

one approach:

tony.kay 2016-07-07T01:22:38.001815Z

1. Query and normalize the data 2. Write the UI and mutations that modify that data 3. Write an "update-table" mutation that transforms (via db->tree and clj->js, etc) into a form usable by the display widget 4. Trigger that mutation with modifications, and include a follow-on read for re-rendering the table

tony.kay 2016-07-07T01:23:08.001816Z

So, you'll basically keep updating a "display version" of the information separate from the database table objects.

tony.kay 2016-07-07T01:23:13.001817Z

A "view", so to speak

curtosis 2016-07-07T01:23:21.001818Z

makes perfect sense

tony.kay 2016-07-07T01:23:34.001819Z

In fact, that's how I imagine it...in SQL terms. There are the real tables, and there are views.

curtosis 2016-07-07T01:23:47.001820Z

so my guess about db->tree wasn't completely wrong?

tony.kay 2016-07-07T01:23:53.001821Z

in what way?

tony.kay 2016-07-07T01:24:06.001822Z

You can certainly use db->tree as the transform tool

tony.kay 2016-07-07T01:24:18.001823Z

you don't have the entire DB in a UI component, so you cannot do it there

curtosis 2016-07-07T01:24:22.001824Z

ah ok

tony.kay 2016-07-07T01:24:57.001825Z

ok..gotta head out. good luck

curtosis 2016-07-07T01:25:27.001826Z

thanks! Got some more ideas to mull over.... and should sleep on it as well. 🙂

curtosis 2016-07-07T15:29:14.001828Z

I can't figure out where/why my data/props are getting rewritten... I have a :post-mutation to put loaded data into the right place for the tab to grab it. Three scenarios: 1) Straightforward untangled style: (assoc-in state [:sources-tab 1 :sources] idents) - works fine, Sources tab component gets the idents. 2) Manually stash the real data: (assoc-in state [:sources-tab 1 :sources] (values sources-by-id)) - data is there in app-state; Sources component gets [{} {} {} {} {} {} {} {} {} {}] in the sources prop 3) Manually stash the real data at app-state root: (assoc-in state [:sources-raw] (values sources-by-id)) - data is there in app-state; Sources component gets [nil nil nil nil nil nil nil nil] in the sources-raw prop (which has a corresponding link '[:sources-raw _] in its query.

curtosis 2016-07-07T15:33:18.001831Z

I could sort of expect #2 if the query is filtering what comes from the app-state, but #3 should be coming through intact, it seems.

2016-07-07T15:49:40.001832Z

I think this might be something I've seen before trying to fight post-mutations - but I'm probably getting my explanation of what I was fighting way wrong: basically it seems like something that has an ident on the component you write the query for must be normalized to be queryable.

2016-07-07T15:51:35.001833Z

I was trying to do something like scenario #3 you describe, and seeing the same thing

curtosis 2016-07-07T15:53:24.001834Z

hmm... but that doesn't make sense to me. I mean, in #3 it's just data I've stashed and could be anything. Why is the query machinery mucking about in it?

2016-07-07T15:54:05.001835Z

I think I just had an array of dumb objects and not even a component with an ident, and it hated that too - just having an array of objects was enough to cause issues, and the fix was to give it the full om treatment (in my case, a list of users, had to build a User component with ident and use that query)

2016-07-07T15:54:21.001836Z

As to the why, I'm not entirely sure, didn't really make sense to me either. 😄

curtosis 2016-07-07T15:55:03.001837Z

I've got another set of random data getting stuffed at app-state root that works fine

curtosis 2016-07-07T15:55:18.001838Z

In a component that also has queries.

2016-07-07T15:55:40.001839Z

Is it a *vector? I was seeing the effect not happen on *maps. (I also had a current-user I was pulling via a link to root query that worked wonderfully)

curtosis 2016-07-07T15:55:51.001840Z

(and accesses that data via a link)

curtosis 2016-07-07T15:56:15.001841Z

mine is an :app-info map

curtosis 2016-07-07T15:56:22.001842Z

and that works like a champ.

curtosis 2016-07-07T15:56:31.001844Z

so you think it's because it's a vector?

2016-07-07T15:56:59.001846Z

Yeah, that was the thing I was fighting for sure - for some reason it didn't act the same because it was a vector

2016-07-07T15:58:33.001847Z

Like some underlying part of the system wanted to force me to normalize anything in a vector, was the sense I got.

2016-07-07T15:59:07.001848Z

Maybe it has to do with the default read untangled provides, or maybe it's underlying how om queries work when they encounter *vectors, not sure

curtosis 2016-07-07T15:59:08.001849Z

just confirmed: a map at app-state root comes through intact via the link

curtosis 2016-07-07T15:59:32.001851Z

yeah, that's really not expected behavior to me - at least not yet 😉

2016-07-07T16:01:17.001852Z

Definitely not for me either - I spent a good half day at least banging my head against my desk with this. 😄

curtosis 2016-07-07T16:01:58.001853Z

can't stuff arbitrary data into the component's map, though (#2) which is, more or less, expected; that part of app-state is "owned" by the component and the query machinery.

2016-07-07T16:02:50.001854Z

I think that's the same root cause - if you put idents there instead, you might have better luck

2016-07-07T16:03:48.001855Z

maps at top level keywords accessed by links will work

2016-07-07T16:03:58.001856Z

maps nested in vectors anywhere in the app-state will not work

2016-07-07T16:04:12.001857Z

it’s an om issue with db->tree

2016-07-07T16:04:20.001858Z

it assumes that vectors contain idents when the query has a join

curtosis 2016-07-07T16:04:40.001860Z

yeah, the idents worked fine (that's #1). My problem is I need to get at the underlying data, and I feel like om/ref->any is the "wrong way"

curtosis 2016-07-07T16:04:56.001861Z

aaaaaaah

2016-07-07T16:05:02.001862Z

I was talking more about #2

2016-07-07T16:05:05.001863Z

I think

2016-07-07T16:06:02.001864Z

I’ve never tried or run into #3

2016-07-07T16:06:32.001865Z

It's basically the same thing because you join - just to a root area instead of further in the tree

2016-07-07T16:06:44.001866Z

oh, ok

2016-07-07T16:06:44.001867Z

I imagine

2016-07-07T16:07:27.001868Z

This definitely seems like a rough edge worth pointing out in the tutorial or something (maybe it was and I didn't pay attention? Lots of good info there)

2016-07-07T16:07:41.001869Z

I wouldn’t be surprised if it were missing

2016-07-07T16:08:03.001870Z

it’s one of those things that’s more of a function of untangled being an alpha framework… operating on top of another alpha framework

2016-07-07T16:08:19.001871Z

we could definitely benefit from a known issues section

2016-07-07T16:08:51.001872Z

Yeah, for sure - but surprisingly this was probably the first thing that acted completely unexpected. Once I got the hang of how queries work, for the most part everything has made a ton of sense.

2016-07-07T16:09:03.001873Z

that’s awesome to hear

2016-07-07T16:09:20.001874Z

the changes tony has mode most recently in 0.5.3 really helped to simplify things too

2016-07-07T16:09:35.001875Z

we’re really happy with how much simpler it’s becoming

2016-07-07T16:10:22.001876Z

@curtosis: did any of that help at all or are you still stuck?

curtosis 2016-07-07T16:11:05.001877Z

I think it helps. 🙂

curtosis 2016-07-07T16:11:14.001878Z

What's confusing me now is where the line is...

curtosis 2016-07-07T16:11:47.001879Z

as in, at app-state root :sources-data [{:key val}{:key val}] will not work

curtosis 2016-07-07T16:12:10.001880Z

but this does: :sources-data {:stuff [{:key val}{:key val}]}

curtosis 2016-07-07T16:12:48.001881Z

is that because :stuff doesn't show up in any component or Root and so om just doesn't care?

2016-07-07T16:16:52.001883Z

I think it's more because you're not joining on :stuff. As long as you don't try to write a join query that looks into the :stuff key you'd probably be okay

2016-07-07T16:17:24.001884Z

So you can treat :stuff as a raw value and that'd probably be okay

2016-07-07T16:18:05.001886Z

But if you tried to go {:stuff [:key]}, then you'd need to normalize the things in that array

2016-07-07T16:20:19.001887Z

It generally makes sense I suppose if you think of any join syntax as its equivalent in SQL - you'd need anything you join to be in normalized into a separate table, so if you do a join in your query, then that's expected behavior. Hopefully that'll help me think about it in case I run into this again.

2016-07-07T16:20:32.001888Z

^^ yes that is my understanding as well

2016-07-07T16:21:03.001889Z

@curtosis: your first example doesn’t work because om assumes that the vector is a list of idents, and tries to denormalize data that is already denormalized

2016-07-07T16:21:22.001890Z

the second one works because :sources-data is a map, not a vector — om doesn’t make assumptions about its structure

curtosis 2016-07-07T16:22:37.001892Z

@ethangracer: right, but the gotcha is that :stuff is a vector, and the magic is that by not invoking any query machinery on :stuff it gets ignored.

2016-07-07T16:23:28.001893Z

yup… definitely an esoteric detail

2016-07-07T16:23:57.001894Z

there is one way it kind of makes sense — if you don’t include a nested query, om just dumps what it finds

2016-07-07T16:24:15.001895Z

if you DO include a nested query, it goes through denormalization

2016-07-07T16:24:31.001896Z

if the data isn’t normalized, there’s no need to add a nested query

curtosis 2016-07-07T16:25:25.001898Z

I keep reminding myself that this is so much simpler once I get through the mind warp. 😛

curtosis 2016-07-07T16:26:39.001899Z

(and, admittedly, my current problem is trying to deal with a 3rd-party react component that doesn't map nicely to my data component hierarchy)

curtosis 2016-07-07T16:28:35.001900Z

the details might still be scroll-up-able, but the short version is: in a normal Sources table you'd iterate over Source rows, no problem. But fixed-data-table doesn't do that; it iterates over cells... so I could either decompose everything down to individual Cell components or just side-load everything in a format that is easier to work with.

curtosis 2016-07-07T16:29:20.001901Z

or punt to om/ref->any to do the lookup.

2016-07-07T17:19:28.001902Z

is there a way to set params for the post-mutation of untangled/load transaction?

2016-07-07T18:14:20.001903Z

I don't think post-mutations can take params @w1ng

2016-07-07T18:25:08.001904Z

@w1ng nope, no post-mutation parameters. what parameters apart from the app-state do you need?

2016-07-07T19:03:08.001905Z

im writing an autocomplete component and want to pass the path where to merge the loaded data

2016-07-07T19:03:49.001906Z

in untangled-todomvc, send-support-request mutation returns the ID in the mutation,

(defmethod apimutate 'support-viewer/send-support-request [e k p]
  {:action
   (fn []
     (let [_ (swap! last-id inc)
           id @last-id]
       (timbre/info "New support request " id)
       (swap! requests assoc id p)
       id))})
how do I get access to that ID in the client?

2016-07-07T19:04:22.001907Z

I want the client to know the request ID, not need to look at the server logs

2016-07-07T19:10:57.001908Z

as a workaround i made a mutation which sets the params i want in the post-mutation in the app state and the post-mutation reads the params from there, but i dont think thats a good solution

2016-07-07T19:25:02.001909Z

@w1ng so you have an autocomplete component that might merge data to different places in the app state depending on the data. currently your workaround is to store that location in app-state so the post-mutation knows where to put the data. am I getting that right?

2016-07-07T19:25:51.001910Z

is there no way to tell where the data belongs based on what’s returned from the server?

2016-07-07T19:27:05.001911Z

just trying to get a sense of how to suggest alternative approaches

2016-07-07T19:28:12.001912Z

@jasonjckn: you should be able to access it from app-state

2016-07-07T19:28:29.001913Z

@ethangracer: where is it loaded?

2016-07-07T19:28:39.001914Z

the return values of action thunks are dropped

2016-07-07T19:28:50.001915Z

hm ok, i'll take a look

2016-07-07T19:28:54.001916Z

oh, you’re not swapping on the app state

2016-07-07T19:29:02.001917Z

I haven’t played much with the support viewer

2016-07-07T19:29:16.001918Z

i think there's no way to get the return value of a remote mutation

2016-07-07T19:29:27.001919Z

i need to use untangled-load

2016-07-07T19:29:32.001920Z

ah

2016-07-07T19:30:22.001921Z

yeah not too sure about the support-viewer

2016-07-07T19:30:42.001922Z

I know that you have to return a map from the mutation, keyed either by :tempids or :value

2016-07-07T19:31:15.001923Z

so the last line of your code above would have to be:

{:value id}

2016-07-07T19:45:10.001924Z

(defmethod mutate 'permalink/create [{:keys [ast state] :as env} k p]
  {:remote (df/remote-load env)
   :action (fn []
             (df/load-data-action state '[:permalink]
                                  :marker false
                                  :params {:permalink {}}))})
gives client-side error
[  1.214s] [om.next] transacted '[(permalink/create)], #uuid "80e58bd9-b41c-4ee8-aa38-844e749449a0"
user.cljs:96 APP-STATE DIFF:  {[:ui/loading-data] [:+ true :- false]}
core.cljs:3081 Uncaught Error: Doesn't support namespace: 

2016-07-07T19:45:31.001925Z

if I change

:permalink {}
to
:permalink nil
it works

2016-07-07T19:47:37.001926Z

same problem with

#(transact! [(untangled/load {:query [:permalink]
                                                                :params {:permalink {}}})])

2016-07-07T19:52:56.001927Z

@ethangracer: yes exactly. the location im storing in the app state has two keys :from and :to . the idea was to have a generic autocomplete component which can handle different types of data (clients, products,...) based on the data passed to it and the transactions passed to onChange

2016-07-07T19:53:11.001928Z

nevermind the issue is on my end, not untangled

2016-07-07T20:01:42.001929Z

@ethangracer: heres my code:

(defmethod m/mutate 'app/set-load-targets [{:keys [state] :as env} k {:keys [from to]}]
  {:action (fn []
             (swap! state assoc-in [:load-data-targets :to] to)
             (swap! state assoc-in [:load-data-targets :from] from)
             )})

(defmethod m/mutate 'app/merge-load-targets [{:keys [state] :as env} k _]
  (let [current-tab (get-in @state [:current-tab 0])
        from (:from (:load-data-targets @state))
        to (:to (:load-data-targets @state))]
    {:action (fn []
               (swap! state assoc-in [current-tab :tab to] (from (:load-data @state)))
               )}))

(autocomplete/ui
 {:floatingLabelText "Clients"
  :onSelect #(m/set-value! this :ui/selectclients-val %)
  :onUpdateInput (fn [search-text data-source]
                   (m/set-value! this :ui/selectclients-text search-text)
                   (om/transact!
                    this
                    `[(app/set-load-targets {:from :clients
                                             :to :ui/selectclients})
                      (untangled/load {:query
                                       ({:load-data
                                         [(:clients
                                           {:fuzzysearch
                                            ~search-text})]})
                                       :refresh [:current-tab]
                                       :post-mutation app/merge-load-targets})
                      :current-tab ]))
  :data (map client-select-item selectclients)
  :searchText selectclients-text})

2016-07-07T20:02:24.001930Z

the autocomplete/ui call is in a component

2016-07-07T20:53:10.001933Z

@jasonjckn: yes, I’ve been seeing this ^^ too

2016-07-07T20:55:30.001935Z

@w1ng: so what you’re saying is you’d like to have some way of getting the :from and :to keys into the mutation. interesting. let me think about that for a bit

2016-07-07T20:56:18.001936Z

your workaround is how I’d approach it as well, need to spin some brain cycles on whether that’s a desirable pattern or not

currentoor 2016-07-07T21:34:40.001937Z

In our app we've got some attributes in client side queries that are not actually retrievable with the pul API. For example the query [:dashboard/title :dashboard/created-at] title works with datomic.api/pull but created-at is derived with a query over the :db/txInstant.

currentoor 2016-07-07T21:36:11.001938Z

Ideally we'd like to have an extensible pull wrapper, call it pluck, where we could specify custom ways to query keys like :dashboard/created-at but the results get merged back in such that it looks like a regular pull API call.

currentoor 2016-07-07T21:37:22.001939Z

@ethangracer @tony.kay: maybe you've run into this usecase? Any suggestions for how to address it?

currentoor 2016-07-07T21:38:26.001940Z

I suppose I should also ask in the datomic channel.

currentoor 2016-07-07T21:56:23.001941Z

I'm thinking an extensible API like so would be useful.

2016-07-07T21:59:11.001943Z

@currentoor: definitely have run into the use case, we are post-processing data on the server to rename the keys as the client expects them to appear

2016-07-07T21:59:40.001944Z

I like the idea of that kind of api

currentoor 2016-07-07T21:59:47.001945Z

@ethangracer: if you change the queries on the client will the server omit them or send back extra data?

2016-07-07T22:00:13.001946Z

I exclude them from the server query using :without on data fetch

2016-07-07T22:00:46.001947Z

then when the server returns the data it normalizes it using the full UI query

2016-07-07T22:01:11.001948Z

oh, actually i’m using a client-side post process as well

currentoor 2016-07-07T22:01:22.001949Z

right we have some issues like that also

currentoor 2016-07-07T22:02:02.001950Z

like querying :dashboard/by-id will always return created-at updated-at whether the client asks for them or not

currentoor 2016-07-07T22:02:47.001951Z

i could go into the server side read function and put some conditional logic for these two keys but that seems way to special-casey

currentoor 2016-07-07T22:03:04.001952Z

a pull api wrapper would be much preferable

2016-07-07T22:03:23.001953Z

let’s think about that then, how would it work

2016-07-07T22:03:32.001954Z

the UI query would have to remain intact for normalization

2016-07-07T22:04:16.001955Z

so either before the query is sent to the server, or after the query hits the server, we would have to intelligently replace keys that correspond to the database schema

2016-07-07T22:04:43.001956Z

then once the data is returned, we’d replace the data returned from the database with the keys required by the client query

2016-07-07T22:05:03.001957Z

missing anything?

currentoor 2016-07-07T22:05:09.001958Z

yeah that sounds about right

currentoor 2016-07-07T22:05:27.001959Z

i was thinking maybe a ring middleware type approach would be appropriate

currentoor 2016-07-07T22:05:34.001960Z

since we need to do pre and post processing

currentoor 2016-07-07T22:05:56.001961Z

with multimethods for the individual keys

2016-07-07T22:05:59.001962Z

which would have access to that pluck api you mentioned?

2016-07-07T22:06:06.001963Z

yeah I think i’m with you

currentoor 2016-07-07T22:06:10.001964Z

yeah

2016-07-07T22:06:17.001965Z

interesting

2016-07-07T22:06:46.001966Z

why is this more preferable than manual modification by query?

currentoor 2016-07-07T22:06:54.001967Z

but the tricky bit is traversing the tree in this ring-middleware thing and then putting stuff back in the right spot

2016-07-07T22:07:02.001968Z

right

currentoor 2016-07-07T22:07:27.001969Z

oh because it might not be just a simple query attribute re-naming

currentoor 2016-07-07T22:07:55.001971Z

like :dashboard/created-at there is not created-at attribute in the dashboard model

currentoor 2016-07-07T22:08:05.001972Z

it's derived from the transaction time

2016-07-07T22:08:56.001973Z

yeah i’m still not totally clear on what you mean by that

2016-07-07T22:09:31.001974Z

it sounds like you have some datomic schema for the namespace dashboard

2016-07-07T22:09:37.001975Z

but created-at is not one of them

2016-07-07T22:09:40.001976Z

correct?

currentoor 2016-07-07T22:09:42.001977Z

yes

2016-07-07T22:09:48.001978Z

ok

2016-07-07T22:10:01.001979Z

so you want to write a custom query to fill that field

currentoor 2016-07-07T22:10:02.001980Z

created at is retrived like so

(d/q '[:find
         ?created-at .
         :in $ ?d
         :where
         [?d :dashboard/organization _ ?org-tx _]
         [?org-tx :db/txInstant ?created-at]]
       db
       eid)

currentoor 2016-07-07T22:10:23.001981Z

but on the client created-at is a regular field like anything else on the dashboard

2016-07-07T22:10:30.001982Z

right

2016-07-07T22:12:08.001983Z

yeah we haven’t run into this exact issue — we have some reporting stats that we calculate using aggregates, so we have to invent keys to return to the client, since there is somewhat significant server-side processing involved in formatting that aggregated data properly

2016-07-07T22:12:34.001984Z

so similar idea, I guess I’m wondering if this api you’re suggesting could be easily extended to support that use case as well

2016-07-07T22:13:10.001985Z

I definitely see how it could be useful, my only remaining question would be where it belongs. untangled-datomic? untangled-server? would this apply to other DB models?

currentoor 2016-07-07T22:18:27.001986Z

my guess would be untangled-datomic unless we can make it super general

currentoor 2016-07-07T22:19:09.001987Z

i'm going to try and pair with @therabidbanana on this

currentoor 2016-07-07T22:19:22.001988Z

hopefully we can come up with something that covers both our usecases

2016-07-07T22:23:48.001989Z

sounds good, let me know if I can do anything to help

2016-07-07T22:32:36.001990Z

@currentoor: snuck in a bit of time from iCTO

2016-07-07T22:33:15.001991Z

2 thoughts. one is that a created-at date derived from a database transaction is unreliable, since any subsequent modification of that attribute would change the transaction time

2016-07-07T22:33:51.001992Z

so a true query to get the initial created-date would be somewhat involved, its easier to store a date as a separate attribute on the entity

2016-07-07T22:34:26.001993Z

second thought: we don’t want to introduce more middleware that is only going to be used for certain kinds of queries, because it’ll just slow server response times for the general use case queries that don’t have this concern

currentoor 2016-07-07T22:40:10.001995Z

Yeah created-at in general should use the history db and grab the smallest timestamp, but for our usecase the organization is only set once and we have validations preventing it from being updated.

currentoor 2016-07-07T23:07:37.001996Z

Also I meant ring middleware style decorator pattern, not actual server middleware.

currentoor 2016-07-07T23:08:05.001998Z

Lol that would be ridiculous! But I can see how what I said was confusing.