Files
rose-ash/lib/datalog/demo.sx
giles e0c7de1a1c
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 27s
datalog: org-chart + transitive headcount demo (219/219)
Adds dl-demo-org-rules: (subordinate Mgr Emp) over a (manager
EMP MGR) graph, and (headcount Mgr N) using count aggregation
grouped by manager. Demonstrates real-world hierarchy queries
(e.g. "everyone reporting up to the CEO") + per-manager rollup.

3 new demo tests: transitive subordinates of CEO (5 entries),
CEO headcount, and direct manager headcount.
2026-05-08 10:24:10 +00:00

163 lines
6.3 KiB
Plaintext

;; lib/datalog/demo.sx — example programs over rose-ash-shaped data.
;;
;; Phase 10 prototypes Datalog as a rose-ash query language. Wiring
;; the EDB to actual PostgreSQL is out of scope for this loop (it
;; would touch service code outside lib/datalog/), but the programs
;; below show the shape of queries we want, and the test suite runs
;; them against synthetic in-memory tuples loaded via dl-program-data.
;;
;; Seven thematic demos:
;;
;; 1. Federation — follow graph, transitive reach, mutuals, FOAF.
;; 2. Content — posts, tags, likes, popularity, "for you" feed.
;; 3. Permissions — group membership and resource access.
;; 4. Cooking-posts — canonical "posts about cooking by people I
;; follow (transitively)" multi-domain query.
;; 5. Tag co-occurrence — distinct (T1, T2) pairs with counts.
;; 6. Shortest path — weighted-DAG path enumeration + min agg.
;; 7. Org chart — transitive subordinate + headcount per mgr.
;; ── Demo 1: federation follow graph ─────────────────────────────
;; EDB: (follows ACTOR-A ACTOR-B) — A follows B.
;; IDB:
;; (mutual A B) — A follows B and B follows A
;; (reachable A B) — transitive follow closure
;; (foaf A C) — friend of a friend (mutual filter)
(define
dl-demo-federation-rules
(quote
((mutual A B <- (follows A B) (follows B A))
(reachable A B <- (follows A B))
(reachable A C <- (follows A B) (reachable B C))
(foaf A C <- (follows A B) (follows B C) (!= A C)))))
;; ── Demo 2: content recommendation ──────────────────────────────
;; EDB:
;; (authored ACTOR POST)
;; (tagged POST TAG)
;; (liked ACTOR POST)
;; IDB:
;; (post-likes POST N) — count of likes per post
;; (popular POST) — posts with >= 3 likes
;; (tagged-by-mutual ACTOR POST) — post tagged TOPIC by someone
;; A's mutuals follow.
(define
dl-demo-content-rules
(quote
((post-likes P N <- (authored Author P) (count N L (liked L P)))
(popular P <- (authored Author P) (post-likes P N) (>= N 3))
(interesting Me P
<-
(follows Me Buddy)
(authored Buddy P)
(popular P)))))
;; ── Demo 3: role-based permissions ──────────────────────────────
;; EDB:
;; (member ACTOR GROUP)
;; (subgroup CHILD PARENT)
;; (allowed GROUP RESOURCE)
;; IDB:
;; (in-group ACTOR GROUP) — direct or via subgroup chain
;; (can-access ACTOR RESOURCE) — actor inherits group permission
(define
dl-demo-perm-rules
(quote
((in-group A G <- (member A G))
(in-group A G <- (member A H) (subgroup-trans H G))
(subgroup-trans X Y <- (subgroup X Y))
(subgroup-trans X Z <- (subgroup X Y) (subgroup-trans Y Z))
(can-access A R <- (in-group A G) (allowed G R)))))
;; ── Demo 4: cooking-posts (the canonical Phase 10 query) ────────
;; "Posts about cooking by people I follow (transitively)."
;; Combines federation (follows + transitive reach), authoring,
;; tagging — the rose-ash multi-domain join.
;;
;; EDB:
;; (follows ACTOR-A ACTOR-B)
;; (authored ACTOR POST)
;; (tagged POST TAG)
(define
dl-demo-cooking-rules
(quote
((reach Me Them <- (follows Me Them))
(reach Me Them <- (follows Me X) (reach X Them))
(cooking-post-by-network Me P
<-
(reach Me Author)
(authored Author P)
(tagged P cooking)))))
;; ── Demo 5: tag co-occurrence ───────────────────────────────────
;; "Posts tagged with both T1 AND T2." Useful for narrowed-down
;; recommendations like "vegetarian cooking" posts.
;;
;; EDB:
;; (tagged POST TAG)
;; IDB:
;; (cotagged POST T1 T2) — post has both T1 and T2 (T1 != T2)
;; (popular-pair T1 T2 N) — count of posts cotagged (T1, T2)
(define
dl-demo-tag-cooccur-rules
(quote
((cotagged P T1 T2 <- (tagged P T1) (tagged P T2) (!= T1 T2))
;; Distinct (T1, T2) pairs that occur somewhere.
(tag-pair T1 T2 <- (cotagged P T1 T2))
(tag-pair-count T1 T2 N
<-
(tag-pair T1 T2)
(count N P (cotagged P T1 T2))))))
;; ── Demo 6: weighted-DAG shortest path ─────────────────────────
;; "What's the cheapest way from X to Y?" Edge weights with `is`
;; arithmetic to sum costs, then `min` aggregation to pick the
;; shortest. Termination requires the graph to be a DAG (cycles
;; would produce infinite distances without a bound; programs
;; built on this should add a depth filter `(<, D, MAX)` if cycles
;; are possible).
;;
;; EDB:
;; (edge FROM TO COST)
;; IDB:
;; (path FROM TO COST) — any path
;; (shortest FROM TO COST) — minimum cost path
(define
dl-demo-shortest-path-rules
(quote
((path X Y W <- (edge X Y W))
(path X Z W
<-
(edge X Y W1)
(path Y Z W2)
(is W (+ W1 W2)))
(shortest X Y W <- (path X Y _) (min W C (path X Y C))))))
;; ── Demo 7: org chart + transitive headcount ───────────────────
;; Manager graph: each employee has a single manager. Compute the
;; transitive subordinate set and headcount per manager.
;;
;; EDB:
;; (manager EMP MGR) — EMP reports directly to MGR
;; IDB:
;; (subordinate MGR EMP) — EMP is in MGR's subtree
;; (headcount MGR N) — number of subordinates under MGR
(define
dl-demo-org-rules
(quote
((subordinate Mgr Emp <- (manager Emp Mgr))
(subordinate Mgr Emp
<- (manager Mid Mgr) (subordinate Mid Emp))
(headcount Mgr N
<- (subordinate Mgr Anyone) (count N E (subordinate Mgr E))))))
;; ── Loader stub ──────────────────────────────────────────────────
;; Wiring to PostgreSQL would replace these helpers with calls into
;; rose-ash's internal HTTP RPC (fetch_data → /internal/data/...).
;; The shape returned by dl-load-from-edb! is the same in either case.
(define
dl-demo-make
(fn
(facts rules)
(dl-program-data facts rules)))