Skip to content

Commit

Permalink
In case of priority draw, pick the node with the most number of input…
Browse files Browse the repository at this point in the history
…s already available
  • Loading branch information
wilkerlucio committed Aug 25, 2024
1 parent 46b3b0a commit 0931f97
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 0931f97

Please sign in to comment.