(defn fetch-cubes!
[datasource cube-query]
...)
(defn fetch-view-data!
[datasource view]
(->> (build-cube-query view)
(fetch-cubes! datasource)
(transform-for-view)))
(defn fetch-cubes!
[datasource cube-query]
...)
(defn fetch-circles!
[datasource circle-query]
...)
;; ...
(defn fetch-view-data!
[datasources view]
(let [cubes (->> (build-cube-query view)
(fetch-cubes! (:cube-source datasources)))
circles (->> (build-circle-query view)
(fetch-circles! (:circle-source datasources)))]
(transform-for-view cubes circles)))
(defn fetch-view-data!
[datasources view]
(let [cubes (->> (build-cube-query view)
(fetch-cubes! (:cube-source datasources))
(future))
circles (->> (build-circle-query view)
(fetch-circles! (:circle-source datasources))
(future))]
(transform-for-view @cubes @circles)))
(defn fetch-view-data!
[datasources view]
(let [floppies (->> (build-floppy-query view)
(fetch-floppies! (:floppy-source datasources)))
gears (->> (extract-gear-references floppies)
(build-gear-subquery view)
(fetch-gears! (:gear-source datasources)))]
(transform-for-view
(attach-gears floppies gears))))
(defn fetch-view-data!
[datasources view]
(let [floppies ...
gears ...
cubes (->> (concat
(extract-floppy-cube-references floppies)
(extract-gear-cube-references gears))
(build-cube-subquery view)
(fetch-cubes! (:cube-source datasources)))]
(transform-for-view
(attach-gears
(attach-floppy-cubes floppies cubes)
(attach-gear-cubes gears cubes)))))
(defn fetch-view-data!
[datasources view]
(let [floppies ...
gears ...
g-cubes (->> (extract-floppy-cube-references floppies)
(build-floppy-cube-subquery view)
(fetch-cubes! (:cube-source datasources)))
f-cubes (->> (extract-gear-cube-references gears)
(build-gear-cube-subquery view)
(fetch-cubes! (:cube-source datasources)))]
(transform-for-view
(attach-gears
(attach-floppy-cubes floppies f-cubes)
(attach-gear-cubes gears g-cubes)))))
(defn fetch-view-data!
[datasources view]
(let [floppies ...
gears ...
g-cubes (->> (extract-floppy-cube-references floppies)
(build-floppy-cube-subquery view)
(fetch-cubes! (:cube-source datasources))
(future))
f-cubes (->> (extract-gear-cube-references gears)
(build-gear-cube-subquery view)
(fetch-cubes! (:cube-source datasources))
(future))]
(transform-for-view
(attach-gears
(attach-floppy-cubes floppies @f-cubes)
(attach-gear-cubes gears @g-cubes)))))
commonFriendsOfFriends id1 id2
concat <$> mapM friendsOf
|
intersect <$> <*>
|
concat <$> mapM friendsOf
|
||
friendsOf id1 |
friendsOf id2 |
data FriendReq a where
FriendsOf :: Id -> FriendReq [Id]
deriving (Typeable)
instance DataSource u FriendReq where
fetch _state _flags _userEnv blockedFetches =
AsyncFetch $ \inner -> do ...
friendsOf :: Id -> Haxl [Id]
friendsOf id = dataFetch (FriendsOf id)
commonFriendsOfFriends id1 id2 = do
friends1 <- friendsOf id1
friends2 <- friendsOf id2
fofs1 <- concat <$> mapM friendsOf friends1
fofs2 <- concat <$> mapM friendsOf friends2
intersect fofs1 fofs2
main = do
env <- ...
id1 <- ...
id2 <- ...
fofs <- runHaxl env $ commonFriendsOfFriends id1 id2
print fofs
cats
library.core.async
channels for concurrency.
(defrecord FriendsOf [id]
muse/DataSource
(fetch [_]
(go
(set (fetch-friend-ids! id)))))
(muse/fmap count (->FriendsOf id1))
(muse/fmap set/intersection (->FriendsOf id1) (->FriendsOf id2))
(->> (->FriendsOf id1)
(muse/fmap first)
(muse/flat-map ->FriendsOf))
(defn friends-of-friends
[id]
(->> (->FriendsOf id)
(muse/traverse ->FriendsOf)
(muse/fmap #(reduce set/union %))))
(defn common-friends-of-friends
[id1 id2]
(muse/fmap
set/intersection
(friends-of-friends id1)
(friends-of-friends id2)))
(muse/run!! (common-friends-of-friends 1 2))
;; => #{3 4 5}
(defrecord FriendsOf [id]
muse/DataSource
(fetch [_]
(go
(set (fetch-friend-ids! id))))
muse/BatchedSource
(fetch-multi [_ users]
(let [ids (cons id (map :id users))]
(->> ids ...))))
(defn user-and-friends-by-name
[name]
(muse/flat-map
(fn [{:keys [id] :as user}]
(muse/fmap
#(assoc user :friends %)
(->FriendsOf id)))
(->User name)))
fmap
map
flat-map
mapcat
Describe your data.
Ask for what you want.
Get predictable results.
{
commonFriends(id1: 1, id2: 2) {
name,
address { city }
}
}
{
"commonFriends": [
{ "name": "Nobody", "address": { "city": "Nowhere" }},
...
]
}
{
commonFriends(id1: 1, id2: 2) {
name,
address { city },
friends { friends { friends { name } } }
}
}
{
"commonFriends": [
{
"name": "Nobody",
"address": { "city": "Nowhere" },
"friends": [{ "friends": [{ "friends": [{ "name": "Who" }]}]}]
},
...
]
}
(engine/run!!
{:friends1 (->FriendsOf 1)
:friends2 (->FriendsOf 2)})
;; => {:friends1 #{2 3}, :friends2 #{2 5}}
(muse/run!!
(muse/fmap
#(hash-map :friends1 %1 :friends2 %2)
(->FriendsOf 1)
(->FriendsOf 2)))
;; => {:friends1 #{2 3}, :friends2 #{2 5}}
(defrecord User [id]
data/Resolvable
(resolve! [_ env]
(d/future
(when-let [user (fetch-user! (:db env) id)]
(assoc user :friends (->FriendsOf (:id user)))))))
fewer (fmap ...)
calls
unnecessary data‽
(defrecord FriendsOf [id]
data/Resolvable
(resolve! [_ env]
(d/future
(let [friends (fetch-friend-ids! (:db env) id)]
(set (map #(->User %) friends))))))
(engine/run!! (->User 1))
;; => IllegalStateException: maximum batch count exceeded (33/32).
{:id projection/leaf
:name projection/leaf
:friends [{:name projection/leaf}]}
(engine/run!! (projection/apply (->User 1) ...))
;; => {:id 1,
;; :name "Someone",
;; :friends [{:name "Nobody"} {:name "NobodyElse"}]}
(projection/union
base-user
{:friends [(projection/extract :name)]})
(engine/run!! (projection/apply (->User 1) ...))
;; => {:id 1,
;; :name "Someone",
;; :friends ["Nobody" "NobodyElse"]}
tree projections are composable!
{:id projection/leaf
:name projection/leaf
:friends (projection/transform count [{}])}
(engine/run!! (projection/apply (->User 1) ...))
;; => {:id 1,
;; :name "Someone",
;; :friends 2}
(def Root
{:user (map->User {})
:users (map->Users {})
...})
(defn fetch-view-data!
[view]
(->> (build-projection view)
(projection/apply Root)
(engine/run!!)))