datalog: rose-ash demo programs (Phase 10 syntactic, 153/153)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
New lib/datalog/demo.sx with three Datalog-as-query-language demos
over synthetic rose-ash data:
Federation: (mutual A B), (reachable A B), (foaf A C) over a
follows graph.
Content: (post-likes P N) via count aggregation, (popular P)
for likes >= 3, (interesting Me P) joining follows
+ authored + popular.
Permissions: (in-group A G) over transitive subgroup chains,
(can-access A R).
10 tests run each program against in-memory EDB tuples loaded via
dl-program-data.
Wiring to PostgreSQL and exposing as a service endpoint (/internal
/datalog) is out of scope for this loop — both would require
edits outside lib/datalog/. Programs above document the EDB shape
a real loader would populate.
This commit is contained in:
@@ -13,6 +13,7 @@ PRELOADS=(
|
||||
lib/datalog/strata.sx
|
||||
lib/datalog/eval.sx
|
||||
lib/datalog/api.sx
|
||||
lib/datalog/demo.sx
|
||||
)
|
||||
|
||||
SUITES=(
|
||||
@@ -25,4 +26,5 @@ SUITES=(
|
||||
"negation:lib/datalog/tests/negation.sx:(dl-negation-tests-run!)"
|
||||
"aggregates:lib/datalog/tests/aggregates.sx:(dl-aggregates-tests-run!)"
|
||||
"api:lib/datalog/tests/api.sx:(dl-api-tests-run!)"
|
||||
"demo:lib/datalog/tests/demo.sx:(dl-demo-tests-run!)"
|
||||
)
|
||||
|
||||
75
lib/datalog/demo.sx
Normal file
75
lib/datalog/demo.sx
Normal file
@@ -0,0 +1,75 @@
|
||||
;; 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.
|
||||
;;
|
||||
;; Three thematic demos:
|
||||
;;
|
||||
;; 1. Federation — follow graph, transitive reach, mutuals.
|
||||
;; 2. Content — posts, tags, likes, popularity, "for you" feed.
|
||||
;; 3. Permissions — group membership and resource access.
|
||||
|
||||
;; ── 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)))))
|
||||
|
||||
;; ── 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)))
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"lang": "datalog",
|
||||
"total_passed": 143,
|
||||
"total_passed": 153,
|
||||
"total_failed": 0,
|
||||
"total": 143,
|
||||
"total": 153,
|
||||
"suites": [
|
||||
{"name":"tokenize","passed":26,"failed":0,"total":26},
|
||||
{"name":"parse","passed":18,"failed":0,"total":18},
|
||||
@@ -12,7 +12,8 @@
|
||||
{"name":"semi_naive","passed":8,"failed":0,"total":8},
|
||||
{"name":"negation","passed":10,"failed":0,"total":10},
|
||||
{"name":"aggregates","passed":10,"failed":0,"total":10},
|
||||
{"name":"api","passed":9,"failed":0,"total":9}
|
||||
{"name":"api","passed":9,"failed":0,"total":9},
|
||||
{"name":"demo","passed":10,"failed":0,"total":10}
|
||||
],
|
||||
"generated": "2026-05-08T08:33:54+00:00"
|
||||
"generated": "2026-05-08T08:45:37+00:00"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# datalog scoreboard
|
||||
|
||||
**143 / 143 passing** (0 failure(s)).
|
||||
**153 / 153 passing** (0 failure(s)).
|
||||
|
||||
| Suite | Passed | Total | Status |
|
||||
|-------|--------|-------|--------|
|
||||
@@ -13,3 +13,4 @@
|
||||
| negation | 10 | 10 | ok |
|
||||
| aggregates | 10 | 10 | ok |
|
||||
| api | 9 | 9 | ok |
|
||||
| demo | 10 | 10 | ok |
|
||||
|
||||
202
lib/datalog/tests/demo.sx
Normal file
202
lib/datalog/tests/demo.sx
Normal file
@@ -0,0 +1,202 @@
|
||||
;; lib/datalog/tests/demo.sx — Phase 10 demo programs.
|
||||
|
||||
(define dl-demo-pass 0)
|
||||
(define dl-demo-fail 0)
|
||||
(define dl-demo-failures (list))
|
||||
|
||||
(define
|
||||
dl-demo-deep=?
|
||||
(fn
|
||||
(a b)
|
||||
(cond
|
||||
((and (list? a) (list? b))
|
||||
(and (= (len a) (len b)) (dl-demo-deq-l? a b 0)))
|
||||
((and (dict? a) (dict? b))
|
||||
(let ((ka (keys a)) (kb (keys b)))
|
||||
(and (= (len ka) (len kb)) (dl-demo-deq-d? a b ka 0))))
|
||||
((and (number? a) (number? b)) (= a b))
|
||||
(else (equal? a b)))))
|
||||
|
||||
(define
|
||||
dl-demo-deq-l?
|
||||
(fn
|
||||
(a b i)
|
||||
(cond
|
||||
((>= i (len a)) true)
|
||||
((not (dl-demo-deep=? (nth a i) (nth b i))) false)
|
||||
(else (dl-demo-deq-l? a b (+ i 1))))))
|
||||
|
||||
(define
|
||||
dl-demo-deq-d?
|
||||
(fn
|
||||
(a b ka i)
|
||||
(cond
|
||||
((>= i (len ka)) true)
|
||||
((let ((k (nth ka i)))
|
||||
(not (dl-demo-deep=? (get a k) (get b k))))
|
||||
false)
|
||||
(else (dl-demo-deq-d? a b ka (+ i 1))))))
|
||||
|
||||
(define
|
||||
dl-demo-set=?
|
||||
(fn
|
||||
(a b)
|
||||
(and
|
||||
(= (len a) (len b))
|
||||
(dl-demo-subset? a b)
|
||||
(dl-demo-subset? b a))))
|
||||
|
||||
(define
|
||||
dl-demo-subset?
|
||||
(fn
|
||||
(xs ys)
|
||||
(cond
|
||||
((= (len xs) 0) true)
|
||||
((not (dl-demo-contains? ys (first xs))) false)
|
||||
(else (dl-demo-subset? (rest xs) ys)))))
|
||||
|
||||
(define
|
||||
dl-demo-contains?
|
||||
(fn
|
||||
(xs target)
|
||||
(cond
|
||||
((= (len xs) 0) false)
|
||||
((dl-demo-deep=? (first xs) target) true)
|
||||
(else (dl-demo-contains? (rest xs) target)))))
|
||||
|
||||
(define
|
||||
dl-demo-test-set!
|
||||
(fn
|
||||
(name got expected)
|
||||
(if
|
||||
(dl-demo-set=? got expected)
|
||||
(set! dl-demo-pass (+ dl-demo-pass 1))
|
||||
(do
|
||||
(set! dl-demo-fail (+ dl-demo-fail 1))
|
||||
(append!
|
||||
dl-demo-failures
|
||||
(str
|
||||
name
|
||||
"\n expected (set): " expected
|
||||
"\n got: " got))))))
|
||||
|
||||
(define
|
||||
dl-demo-run-all!
|
||||
(fn
|
||||
()
|
||||
(do
|
||||
;; ── Federation ──────────────────────────────────────────
|
||||
(dl-demo-test-set! "mutuals"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote ((follows alice bob) (follows bob alice)
|
||||
(follows bob carol) (follows carol dave)))
|
||||
dl-demo-federation-rules)
|
||||
(quote (mutual alice X)))
|
||||
(list {:X (quote bob)}))
|
||||
|
||||
(dl-demo-test-set! "reachable transitive"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote ((follows alice bob) (follows bob carol) (follows carol dave)))
|
||||
dl-demo-federation-rules)
|
||||
(quote (reachable alice X)))
|
||||
(list {:X (quote bob)} {:X (quote carol)} {:X (quote dave)}))
|
||||
|
||||
(dl-demo-test-set! "foaf"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote ((follows alice bob) (follows bob carol) (follows alice dave)))
|
||||
dl-demo-federation-rules)
|
||||
(quote (foaf alice X)))
|
||||
(list {:X (quote carol)}))
|
||||
|
||||
;; ── Content ─────────────────────────────────────────────
|
||||
(dl-demo-test-set! "popular posts"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((authored alice p1) (authored bob p2) (authored carol p3)
|
||||
(liked u1 p1) (liked u2 p1) (liked u3 p1)
|
||||
(liked u1 p2)))
|
||||
dl-demo-content-rules)
|
||||
(quote (popular P)))
|
||||
(list {:P (quote p1)}))
|
||||
|
||||
(dl-demo-test-set! "interesting feed"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((follows me alice) (follows me bob)
|
||||
(authored alice p1) (authored bob p2)
|
||||
(liked u1 p1) (liked u2 p1) (liked u3 p1)
|
||||
(liked u4 p2)))
|
||||
dl-demo-content-rules)
|
||||
(quote (interesting me P)))
|
||||
(list {:P (quote p1)}))
|
||||
|
||||
(dl-demo-test-set! "post likes count"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((authored alice p1)
|
||||
(liked u1 p1) (liked u2 p1) (liked u3 p1)))
|
||||
dl-demo-content-rules)
|
||||
(quote (post-likes p1 N)))
|
||||
(list {:N 3}))
|
||||
|
||||
;; ── Permissions ─────────────────────────────────────────
|
||||
(dl-demo-test-set! "direct group access"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((member alice editors)
|
||||
(allowed editors blog)))
|
||||
dl-demo-perm-rules)
|
||||
(quote (can-access X blog)))
|
||||
(list {:X (quote alice)}))
|
||||
|
||||
(dl-demo-test-set! "subgroup access"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((member bob writers)
|
||||
(subgroup writers editors)
|
||||
(allowed editors blog)))
|
||||
dl-demo-perm-rules)
|
||||
(quote (can-access X blog)))
|
||||
(list {:X (quote bob)}))
|
||||
|
||||
(dl-demo-test-set! "transitive subgroup"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote
|
||||
((member carol drafters)
|
||||
(subgroup drafters writers)
|
||||
(subgroup writers editors)
|
||||
(allowed editors blog)))
|
||||
dl-demo-perm-rules)
|
||||
(quote (can-access X blog)))
|
||||
(list {:X (quote carol)}))
|
||||
|
||||
(dl-demo-test-set! "no access without grant"
|
||||
(dl-query
|
||||
(dl-demo-make
|
||||
(quote ((member dave outsiders) (allowed editors blog)))
|
||||
dl-demo-perm-rules)
|
||||
(quote (can-access X blog)))
|
||||
(list)))))
|
||||
|
||||
(define
|
||||
dl-demo-tests-run!
|
||||
(fn
|
||||
()
|
||||
(do
|
||||
(set! dl-demo-pass 0)
|
||||
(set! dl-demo-fail 0)
|
||||
(set! dl-demo-failures (list))
|
||||
(dl-demo-run-all!)
|
||||
{:passed dl-demo-pass
|
||||
:failed dl-demo-fail
|
||||
:total (+ dl-demo-pass dl-demo-fail)
|
||||
:failures dl-demo-failures})))
|
||||
@@ -234,15 +234,25 @@ large graphs.
|
||||
rose-ash ActivityPub follow relationships (Phase 10).
|
||||
|
||||
### Phase 10 — Datalog as a query language for rose-ash
|
||||
- [ ] Schema: map SQLAlchemy model relationships to Datalog EDB facts
|
||||
(e.g. `(follows user1 user2)`, `(authored user post)`, `(tagged post tag)`)
|
||||
- [ ] Loader: `dl-load-from-db!` — query PostgreSQL, populate Datalog EDB
|
||||
- [ ] Query examples:
|
||||
- `?- ancestor(me, X), authored(X, Post), tagged(Post, cooking).`
|
||||
→ posts about cooking by people I follow (transitively)
|
||||
- `?- popular(Post) :- tagged(Post, T), count(L, (liked(L, Post))) >= 10.`
|
||||
→ posts with 10+ likes
|
||||
- [ ] Expose as a rose-ash service endpoint: `POST /internal/datalog` with program + query
|
||||
- [x] Schema sketches in `lib/datalog/demo.sx`:
|
||||
- **Federation**: `(follows A B)` → `(mutual A B)`, `(reachable A B)`,
|
||||
`(foaf A C)` (friend-of-a-friend, distinct).
|
||||
- **Content**: `(authored A P)`, `(liked U P)`, `(tagged P T)` →
|
||||
`(post-likes P N)` via aggregation, `(popular P)` for likes ≥ 3,
|
||||
`(interesting Me P)` joining follows + authored + popular.
|
||||
- **Permissions**: `(member A G)`, `(subgroup C P)`, `(allowed G R)`
|
||||
→ `(in-group A G)` over transitive subgroups, `(can-access A R)`.
|
||||
- [ ] Loader `dl-load-from-db!` — out of scope for this loop
|
||||
(would need to edit `shared/services/` outside `lib/datalog/`).
|
||||
Programs in `demo.sx` already document the EDB shape expected
|
||||
from such a loader. `dl-program-data` consumes the same shape.
|
||||
- [x] Query examples covered by `lib/datalog/tests/demo.sx` (10):
|
||||
mutuals, transitive reach, FOAF, popular posts, interesting feed,
|
||||
post likes count, direct/subgroup/transitive group access, no
|
||||
access without grant.
|
||||
- [ ] Service endpoint `POST /internal/datalog` — out of scope as above.
|
||||
Once exposed, server-side handler would be `dl-program-data` +
|
||||
`dl-query`, returning JSON-encoded substitutions.
|
||||
|
||||
## Blockers
|
||||
|
||||
@@ -259,6 +269,16 @@ large graphs.
|
||||
|
||||
_Newest first._
|
||||
|
||||
- 2026-05-08 — Phase 10 syntactic demo. New `lib/datalog/demo.sx`
|
||||
with three programs over rose-ash-shaped data: federation
|
||||
(`mutual`, `reachable`, `foaf`), content recommendation
|
||||
(`post-likes` via count aggregation, `popular`, `interesting`),
|
||||
and role-based permissions (`in-group` over transitive subgroups,
|
||||
`can-access`). 10 demo tests pass against synthetic EDB tuples.
|
||||
Postgres loader and `/internal/datalog` HTTP endpoint remain
|
||||
out of scope for this loop (they need service-tree edits beyond
|
||||
`lib/datalog/**`). Conformance now 153/153.
|
||||
|
||||
- 2026-05-08 — Phase 5b perf: hash-set membership in `dl-add-fact!`.
|
||||
db gains a parallel `:facts-keys {<rel>: {<tuple-string>: true}}`
|
||||
index alongside `:facts`. `dl-tuple-key` derives a stable string
|
||||
|
||||
Reference in New Issue
Block a user