Skip to content

Commit

Permalink
Merge pull request #219 from wilkerlucio/issue-202
Browse files Browse the repository at this point in the history
In case of priority draw, pick the node with the most number of input…
  • Loading branch information
wilkerlucio authored Aug 25, 2024
2 parents 46b3b0a + 0931f97 commit 3e01d28
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Fix spec for `pco/?`
- Merge params when merging nodes on planner (issue #216)
- Ensure all instances of same resolver in same graph have same params (issue #211)
- In case of priority draw, pick the node with the most number of inputs already available (issue #202)

## [2023.08.22-alpha]
- BREAKING: `::p.error/missing-output` is now converged to `::p.error/attribute-missing` (issue #149)
Expand Down
30 changes: 23 additions & 7 deletions src/main/com/wsscode/pathom3/connect/runner.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -573,10 +573,11 @@
(merge-node-stats! env node {::node-run-finish-ms (time/now-ms)})
nil)))))

(defn priority-sort
"Find the node path with the highest priority to run. Returns a set of the candidates
with the same priority level."
[{::pcp/keys [graph] :as env} or-node node-ids]
(defn pick-node-highest
"Starting from possible paths, find the nodes responsible for the attribute required by the
OR node. Now compute the criteria from each node config it must result in a number. Keep only
the nodes that have the highest criteria value."
[{::pcp/keys [graph] :as env} or-node node-ids criteria]
(let [expects (-> or-node ::pcp/expects keys)
nodes (for [id node-ids
successor (pcp/node-successors graph id)
Expand All @@ -585,13 +586,26 @@
:when (and provides (every? provides expects))]
[id config])]
(if (seq nodes)
(let [[id config] (apply max-key #(-> % second (::pco/priority 0)) nodes)]
(let [[id config] (apply max-key #(-> % second criteria) nodes)]
(if id
(into #{} (comp (filter #(= (::pco/priority config) (::pco/priority (second %))))
(into #{} (comp (filter #(= (criteria config) (criteria (second %))))
(map first)) nodes)
node-ids))
#{})))

(defn priority-sort
"Find the node path with the highest priority to run. Returns a set of the candidates
with the same priority level."
[env or-node node-ids]
(pick-node-highest env or-node node-ids #(::pco/priority % 0)))

(defn input-size-sort
"Find the nodes with highest input size and removes any node with sizes smaller than it."
[env or-node node-ids]
(let [available (-> env p.ent/entity pfsd/data->shape-descriptor-shallow)]
(pick-node-highest env or-node node-ids
#(-> % ::pcp/input (pfsd/intersection available) count))))

(defn node-weight
"Sums up the weight of a node and its successors"
[{::pcp/keys [graph]
Expand Down Expand Up @@ -619,7 +633,9 @@
(first candidates)))

(defn default-choose-path [env or-node node-ids]
(let [candidates (priority-sort env or-node node-ids)
(let [candidates (->> node-ids
(priority-sort env or-node)
(input-size-sort env or-node))
cc (count candidates)]
(cond
(> cc 1)
Expand Down
96 changes: 96 additions & 0 deletions test/com/wsscode/pathom3/connect/runner/helpers.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
(ns com.wsscode.pathom3.connect.runner.helpers
(:require
[check.core :refer [#_ :clj-kondo/ignore => check]]
[clojure.spec.alpha :as s]
[clojure.test :refer [testing]]
[com.wsscode.pathom3.connect.runner :as pcr]
[com.wsscode.pathom3.connect.runner.async :as pcra]
[com.wsscode.pathom3.connect.runner.parallel :as pcrc]
[com.wsscode.pathom3.entity-tree :as p.ent]
[com.wsscode.pathom3.format.eql :as pf.eql]
[edn-query-language.core :as eql]
[matcher-combinators.test]
[promesa.core :as p])
#?(:cljs
(:require-macros
[com.wsscode.pathom3.connect.runner.helpers])))

(defn match-keys? [ks]
(fn [m]
(reduce
(fn [_ k]
(if-let [v (find m k)]
(if (s/valid? k (val v))
true
(reduced false))
(reduced false)))
true
ks)))

(defn run-graph
([{::keys [map-select?] :as env} tree query]
(let [ast (eql/query->ast query)
res (pcr/run-graph! env ast (p.ent/create-entity tree))]
(if map-select?
(pf.eql/map-select env res query)
res)))
([env tree query _] (run-graph env tree query)))

(defn run-graph-async
([env tree query]
(let [ast (eql/query->ast query)]
(pcra/run-graph! env ast (p.ent/create-entity tree))))
([env tree query _expected]
(run-graph-async env tree query)))

(defn run-graph-parallel [env tree query]
(let [ast (eql/query->ast query)]
(p/timeout
(pcrc/run-graph! env ast (p.ent/create-entity tree))
3000)))

(def all-runners [run-graph #?@(:clj [run-graph-async run-graph-parallel])])

#?(:clj
(defmacro check-serial [env entity tx expected]
`(check
(run-graph ~env ~entity ~tx)
~'=> ~expected))

:cljs
(defn check-serial [env entity tx expected]
(check
(run-graph env entity tx)
=> expected)))

#?(:clj
(defmacro check-parallel [env entity tx expected]
`(check
@(run-graph-parallel ~env ~entity ~tx)
~'=> ~expected))

:cljs
(defn check-parallel [env entity tx expected]
(check
@(run-graph-parallel env entity tx)
=> expected)))

#?(:clj
(defmacro check-all-runners [env entity tx expected]
`(doseq [runner# all-runners]
(testing (str runner#)
(check
(let [res# (runner# ~env ~entity ~tx)]
(if (p/promise? res#)
@res# res#))
~'=> ~expected))))

:cljs
(defn check-all-runners [env entity tx expected]
(doseq [runner all-runners]
(testing (str runner)
(check
(let [res (runner env entity tx)]
(if (p/promise? res)
@res res))
=> expected)))))
31 changes: 31 additions & 0 deletions test/com/wsscode/pathom3/connect/runner/path_selection.cljc
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
(ns com.wsscode.pathom3.connect.runner.path-selection
(:require
[clojure.test :refer [deftest]]
[com.wsscode.pathom3.connect.indexes :as pci]
[com.wsscode.pathom3.connect.operation :as pco]
[com.wsscode.pathom3.connect.runner.helpers :as rr :refer [check-all-runners]]))

(deftest run-graph-input-size-sort
(let [env (pci/register
[(pco/resolver 'resolve-full-name-by-first-name
{::pco/input [:person/first-name]
::pco/output [:person/full-name]}
(fn [_ {:person/keys [first-name]}]
{:person/full-name first-name}))

(pco/resolver 'resolve-full-name-with-first-and-last-name
{::pco/input [:person/first-name :person/last-name]
::pco/output [:person/full-name]}
(fn [_ {:person/keys [first-name last-name]}]
{:person/full-name (str first-name " " last-name)}))])]
(check-all-runners
env
{:person/first-name "Björn", :person/last-name "Ebbinghaus"}
[:person/full-name]
{:person/full-name "Björn Ebbinghaus"})

(check-all-runners
env
{:person/first-name "Björn"}
[:person/full-name]
{:person/full-name "Björn"})))

0 comments on commit 3e01d28

Please sign in to comment.