untangled

NEW CHANNEL: #fulcro
wilkerlucio 2016-11-04T11:49:48.003707Z

hello, for the people using Datomic, how are you handling union queries on the server side?

2016-11-04T13:07:03.003709Z

@wilkerlucio we’re only using union queries for view routing on the client, so we never need to send those queries to the server. all the queries we send contain only props and joins

wilkerlucio 2016-11-04T13:12:24.003711Z

@ethangracer thanks, and you have thoughts on how to integrate that with pull syntax? on my previous project I was fully controlling the parsing, so was kind easy to implement, I can still go this way but I rather find a way that requires less coding

2016-11-04T13:16:55.003712Z

I wouldn’t describe it as “integrating with pull syntax”, but what we do is we set up all of the UI relevant concerns with InitialAppState (which include all of the union queries). Then we’ll do something like (load-data this [{:people (om/get-query Person)}]), which would build a list of people idents at :people normalized to :people/by-id. So when we need to access all people, we do so by adding [:people ‘_] to the query of the component that needs the data

2016-11-04T13:17:40.003713Z

So we circumvent the need to have the data placed in the app state corresponding with the UI component structure (which would require the union logic server-side that you’re talking about to return the data in the right format)

wilkerlucio 2016-11-04T13:18:05.003714Z

humm, my need is in the heterogeneos list style of query

wilkerlucio 2016-11-04T13:18:41.003715Z

I think it needs to be processed on the server side, otherwise I would be required to overfetch (and in my case that can be a disaster, some of the types requires much more data than others)

2016-11-04T13:19:29.003717Z

what do you mean by “heterogeneous list style?"

2016-11-04T13:19:41.003718Z

entities with different attributes in the same list?

wilkerlucio 2016-11-04T13:19:47.003719Z

exatly

2016-11-04T13:19:54.003720Z

gotcha

2016-11-04T13:20:32.003721Z

I’m not sure off the top of my head if there’s a way to do that without server-side processing

2016-11-04T13:20:48.003722Z

And I’m not sure how a union query would handle normalizing that data

wilkerlucio 2016-11-04T13:20:55.003723Z

I don't think would be possible without it, the server needs to decide the fields while fetching each entry

2016-11-04T13:20:56.003724Z

just because I haven’t tried it 🙂

wilkerlucio 2016-11-04T13:22:02.003725Z

I know how to handle the client side, I did it before, just the server is being a scratch on the head (you can see more for the client here: https://github.com/omcljs/om/wiki/Queries-With-Unions)

2016-11-04T13:22:02.003726Z

I suppose on the server you could write a query that pulled on set of attributes or another based on what attribute was missing from the entity that you’re pulling

2016-11-04T13:25:37.003732Z

i.e.

[:find ?e
 :where 
(or-join [?e]
  (and
    [(missing $ ?e :attr-type/two)]
    [?e :attr-type/one ?value])
  (and
   [(missing $ ?e :attr-type/one)]
   [?e :attr-type/two ?value]))

2016-11-04T13:25:53.003734Z

but that gets super ugly the more entity options there are

wilkerlucio 2016-11-04T13:26:17.003735Z

and it ties the query to the attributes... not very good, we need the client on this decision

2016-11-04T13:27:06.003736Z

yeah I would definitely vote for a post-mutation

2016-11-04T13:27:21.003738Z

do a separate query for each type

2016-11-04T13:27:25.003739Z

then manually merge the lists of idents into one list

wilkerlucio 2016-11-04T13:28:19.003742Z

I think doing that would be a pain to join the items in order after, would need a separated field just to keep the sorting (that might change depending on occasion)

2016-11-04T13:28:31.003743Z

oh, it’s order dependent!

wilkerlucio 2016-11-04T13:28:32.003744Z

I believe the best solution is to the server to process the union as is

wilkerlucio 2016-11-04T13:28:41.003745Z

somehow

2016-11-04T13:29:32.003746Z

In that case I’m not sure that you have an option apart from manually parsing it server-side

2016-11-04T13:29:39.003747Z

but you’re now out my depth!

wilkerlucio 2016-11-04T13:30:44.003748Z

on the other project I used something I called :union-selector, so in my manual parsing I check for this option, when it's present and the current query is an union, it would use the value of :union-selector as the key (which could be a regular attribute on the field, or a virtual one), to select which query to fetch the rest

wilkerlucio 2016-11-04T13:31:05.003749Z

works greatly, but then I have to re-implement most of the pull syntax myself

2016-11-04T13:35:18.003751Z

honestly not completely following but I understand the need to split up the union query into its constituent pull syntax fragments to get the data from datomic

2016-11-04T13:38:54.003753Z

@wilkerlucio with regards to the sorting, why not just do that client side? why does it need to come back from the server sorted? if you don’t need the data to be sorted by the server, then you can use the post-mutation method and just sort the list that way

wilkerlucio 2016-11-04T13:40:36.003754Z

@ethangracer it's possible, but I ultimaly I think the union syntax is a good query syntax, and by using it I avoid a lot of those manual transformations that will be hard to understand (and imagine a project with multiple of those across the UI)

wilkerlucio 2016-11-04T13:40:48.003755Z

I think solving on the server is the simplest solution

2016-11-04T13:41:32.003757Z

fair enough. I’d be curious to see a snippet for how you're handling that at some point if you don’t mind sharing

wilkerlucio 2016-11-04T13:42:26.003759Z

sure, no problem with sharing at all 🙂

tony.kay 2016-11-04T18:21:01.003766Z

@wilkerlucio Handling it on the server is why we left you to write parser emitters on the server. As long as you return your hetero list from the server, you can use the union query on the client to normalize the result.

tony.kay 2016-11-04T18:21:42.003767Z

So, perfectly normal to want to send a union query to the server, but yeah Datomic won't just "get it"

wilkerlucio 2016-11-04T18:23:07.003768Z

@tony.kay we, I'm getting to think that a custom pull reader is going to be required by a lot of applications, datomic pull is great, but not flexible/extensible enough, I'm porting some of the engine that I did before in Node.js to Clojure, it might get to be a lib one day 🙂

wilkerlucio 2016-11-04T18:24:09.003770Z

the datomic entities help a lot on that

tony.kay 2016-11-04T18:25:23.003771Z

In general, it would be nice to have something that does that repetitive work for both SQL and Datomic

tony.kay 2016-11-04T18:25:35.003772Z

with just some security plugins

currentoor 2016-11-04T18:25:51.003773Z

@wilkerlucio we also felt datomic pull was great but not flexible/extensible enough

currentoor 2016-11-04T18:25:55.003774Z

so we wrote this https://github.com/AdStage/pluck-api

wilkerlucio 2016-11-04T18:26:41.003776Z

@currentoor thanks, I'll check it out

2016-11-04T19:29:43.003781Z

What's the best practice to store blob files e.g. images due the lack of support for blob files in datomic? I just saw that the adstage pluck-api mention the use of a blob store in the example snippets.

2016-11-04T19:32:23.003782Z

At AdStage we just generate a UUID and store them as serialized bytes to postgres (using Nippy), then use the pluck api to join the data back in as if it was a key you could pull with datomic. As datomic makes indexes on values, this was the approach recommended to us in the #datomic channel, rather than trying to serialize to string and store as a value in datomic.

2016-11-04T19:36:15.003783Z

Sounds good for me. Thanks

2016-11-04T19:40:11.003784Z

(Well, to be clear, just the "don't store in datomic" part was recommended, we arrived at the "store in postgres" thing mainly because we were already setting it up as our underlying datomic data store, so easy to add an extra table somewhere...)

2016-11-04T20:30:27.003786Z

That's reasonable. I also have to checkout the image-library component in untangled-component

2016-11-04T20:31:19.003787Z

But the pluck-api/blobstore solution is more general in case for storing other types of files for e.g PDFs and so on.

currentoor 2016-11-04T20:32:37.003789Z

@baris yeah you should be able to store anything with the pluck API and still retain the niceness of the datomic pull query

currentoor 2016-11-04T20:33:31.003790Z

the pluck-api doesn’t yet support pluck-many, analogous to pull-many for collections, but for pull it works pretty well for us

2016-11-04T20:34:49.003792Z

That's pretty cool. I'll give it a try.

currentoor 2016-11-04T20:35:11.003793Z

awesome, let me know what you think 😄

2016-11-04T20:35:23.003794Z

For sure

2016-11-04T20:38:02.003795Z

thought i’d drop a quick note in here to let people know that as much as I’ve enjoyed working on Untangled, and as much as I hope that I’ll continue to work on it, I’ve accepted a new job that isn’t working in Clojure. So I’m not sure how useful I’ll be as the framework continues to grow since I won’t be developing with it full-time or professionally. In any case, it has been a pleasure getting to know everyone here and we’ll see what happens. The rest of the team is still around and I’m still going to evangelize untangled as much as I can 🙂

currentoor 2016-11-04T20:42:25.003797Z

@ethangracer sorry to see you go. I really appreciated your push for documentation. Good luck with the new job!

2016-11-04T20:42:56.003798Z

@ethangracer: All good for your future career...thanks for pushing untangled

wilkerlucio 2016-11-04T21:23:23.003799Z

@ethangracer good luck on your new job, and thanks for all your contributions so far 🙂

2016-11-04T21:55:40.003801Z

thanks everyone!

wilkerlucio 2016-11-04T22:32:21.003803Z

@ethangracer I just finished the basic implementation of my parser, supporting union queries, the implementation uses an idea of "portable nodes" you can say, since I started using Om.next I always though on my server as a graph, and I would like to be able to specify nodes in a way that I could re-use then at any node point on my graph, and being able to compose those, to solve this I end up with the idea of "readers", the readers can be functions (simplest one, just receives the env) maps (keys are the attribute to access, values are functions that takes env) and sequences (can express a series of readers, readers can stop there to return a value, or return ::continue to move into the next reader, like ring or something), with these ideas I got a small library for those readers, and they are the base for the rest:

wilkerlucio 2016-11-04T22:32:45.003805Z

then to use it:

wilkerlucio 2016-11-04T22:33:40.003807Z

the important bit here is read-memos, it sets the ::union-selector to use :memo/type as the field to figure which branch of union to go, the rest is generic and can be reused

2016-11-04T23:20:49.003811Z

@ethangracer congratulations! just out of curiosity, what are you working on now if not clojure?