rust-kgdb 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -23
- package/examples/core-concepts-demo.ts +50 -35
- package/examples/datalog-example.ts +246 -339
- package/examples/fraud-detection-agent.js +23 -36
- package/examples/hypermind-fraud-underwriter.ts +54 -51
- package/examples/underwriting-agent.js +16 -20
- package/hypermind-agent.js +1003 -2347
- package/index.d.ts +79 -0
- package/package.json +1 -1
- package/rust-kgdb-napi.darwin-x64.node +0 -0
|
@@ -2,61 +2,68 @@
|
|
|
2
2
|
* Datalog Example for rust-kgdb TypeScript SDK
|
|
3
3
|
*
|
|
4
4
|
* Demonstrates Datalog reasoning capabilities including:
|
|
5
|
-
* - Adding facts and rules
|
|
5
|
+
* - Adding facts and rules using JSON format
|
|
6
6
|
* - Semi-naive evaluation
|
|
7
|
-
* - Recursive rule evaluation
|
|
8
|
-
* -
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* - Recursive rule evaluation (transitive closure)
|
|
8
|
+
* - Querying derived facts
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: The DatalogProgram API uses JSON strings for facts and rules.
|
|
11
|
+
* This enables a flexible, language-agnostic interface.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
import {
|
|
14
|
+
import { DatalogProgram, evaluateDatalog, queryDatalog, GraphDB } from 'rust-kgdb';
|
|
14
15
|
|
|
15
16
|
// =============================================================================
|
|
16
17
|
// Example 1: Basic Facts and Rules
|
|
17
18
|
// =============================================================================
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
function basicDatalogExample() {
|
|
20
21
|
console.log('=== Basic Datalog Facts and Rules ===\n');
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
+
const program = new DatalogProgram();
|
|
23
24
|
|
|
24
25
|
// Add facts: parent(X, Y) means X is parent of Y
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
// Facts use JSON format: {"predicate": "name", "terms": ["arg1", "arg2"]}
|
|
27
|
+
program.addFact(JSON.stringify({ predicate: 'parent', terms: ['alice', 'bob'] }));
|
|
28
|
+
program.addFact(JSON.stringify({ predicate: 'parent', terms: ['alice', 'carol'] }));
|
|
29
|
+
program.addFact(JSON.stringify({ predicate: 'parent', terms: ['bob', 'david'] }));
|
|
30
|
+
program.addFact(JSON.stringify({ predicate: 'parent', terms: ['bob', 'eve'] }));
|
|
31
|
+
program.addFact(JSON.stringify({ predicate: 'parent', terms: ['carol', 'frank'] }));
|
|
30
32
|
|
|
31
33
|
console.log('Facts added:');
|
|
32
34
|
console.log(' parent(alice, bob)');
|
|
33
35
|
console.log(' parent(alice, carol)');
|
|
34
36
|
console.log(' parent(bob, david)');
|
|
35
37
|
console.log(' parent(bob, eve)');
|
|
36
|
-
console.log(' parent(carol, frank)
|
|
38
|
+
console.log(' parent(carol, frank)');
|
|
39
|
+
console.log(` Total facts: ${program.factCount()}\n`);
|
|
37
40
|
|
|
38
41
|
// Add rule: grandparent(X, Z) :- parent(X, Y), parent(Y, Z)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
['X', 'Z'],
|
|
42
|
-
[
|
|
43
|
-
{ predicate: 'parent',
|
|
44
|
-
{ predicate: 'parent',
|
|
42
|
+
// Rules use JSON format: {"head": {...}, "body": [...]}
|
|
43
|
+
program.addRule(JSON.stringify({
|
|
44
|
+
head: { predicate: 'grandparent', terms: ['X', 'Z'] },
|
|
45
|
+
body: [
|
|
46
|
+
{ predicate: 'parent', terms: ['X', 'Y'] },
|
|
47
|
+
{ predicate: 'parent', terms: ['Y', 'Z'] }
|
|
45
48
|
]
|
|
46
|
-
);
|
|
49
|
+
}));
|
|
47
50
|
|
|
48
51
|
console.log('Rule added:');
|
|
49
|
-
console.log(' grandparent(X, Z) :- parent(X, Y), parent(Y, Z)
|
|
52
|
+
console.log(' grandparent(X, Z) :- parent(X, Y), parent(Y, Z)');
|
|
53
|
+
console.log(` Total rules: ${program.ruleCount()}\n`);
|
|
50
54
|
|
|
51
|
-
// Evaluate to derive all facts
|
|
52
|
-
|
|
55
|
+
// Evaluate to derive all facts using semi-naive evaluation
|
|
56
|
+
const evalResult = evaluateDatalog(program);
|
|
57
|
+
console.log('Evaluation completed.');
|
|
58
|
+
console.log(` Result: ${evalResult}\n`);
|
|
53
59
|
|
|
54
60
|
// Query grandparents
|
|
55
|
-
const grandparents =
|
|
61
|
+
const grandparents = queryDatalog(program, 'grandparent');
|
|
62
|
+
const results = JSON.parse(grandparents);
|
|
56
63
|
console.log('Query: grandparent(?who, ?grandchild)');
|
|
57
64
|
console.log('Results:');
|
|
58
|
-
for (const result of
|
|
59
|
-
console.log(` ${result.
|
|
65
|
+
for (const result of results) {
|
|
66
|
+
console.log(` ${result.terms[0]} is grandparent of ${result.terms[1]}`);
|
|
60
67
|
}
|
|
61
68
|
console.log();
|
|
62
69
|
}
|
|
@@ -65,173 +72,214 @@ async function basicDatalogExample() {
|
|
|
65
72
|
// Example 2: Recursive Rules (Transitive Closure)
|
|
66
73
|
// =============================================================================
|
|
67
74
|
|
|
68
|
-
|
|
75
|
+
function recursiveDatalogExample() {
|
|
69
76
|
console.log('=== Recursive Rules (Transitive Closure) ===\n');
|
|
70
77
|
|
|
71
|
-
const
|
|
78
|
+
const program = new DatalogProgram();
|
|
72
79
|
|
|
73
80
|
// Add edge facts for a graph
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
program.addFact(JSON.stringify({ predicate: 'edge', terms: ['a', 'b'] }));
|
|
82
|
+
program.addFact(JSON.stringify({ predicate: 'edge', terms: ['b', 'c'] }));
|
|
83
|
+
program.addFact(JSON.stringify({ predicate: 'edge', terms: ['c', 'd'] }));
|
|
84
|
+
program.addFact(JSON.stringify({ predicate: 'edge', terms: ['d', 'e'] }));
|
|
85
|
+
program.addFact(JSON.stringify({ predicate: 'edge', terms: ['a', 'c'] })); // shortcut
|
|
79
86
|
|
|
80
87
|
console.log('Graph edges: a->b, b->c, c->d, d->e, a->c\n');
|
|
81
88
|
|
|
82
89
|
// Base case: path(X, Y) :- edge(X, Y)
|
|
83
|
-
|
|
84
|
-
'path',
|
|
85
|
-
['X', 'Y']
|
|
86
|
-
|
|
87
|
-
);
|
|
90
|
+
program.addRule(JSON.stringify({
|
|
91
|
+
head: { predicate: 'path', terms: ['X', 'Y'] },
|
|
92
|
+
body: [{ predicate: 'edge', terms: ['X', 'Y'] }]
|
|
93
|
+
}));
|
|
88
94
|
|
|
89
95
|
// Recursive case: path(X, Z) :- edge(X, Y), path(Y, Z)
|
|
90
|
-
|
|
91
|
-
'path',
|
|
92
|
-
[
|
|
93
|
-
|
|
94
|
-
{ predicate: '
|
|
95
|
-
{ predicate: 'path', args: ['Y', 'Z'] }
|
|
96
|
+
program.addRule(JSON.stringify({
|
|
97
|
+
head: { predicate: 'path', terms: ['X', 'Z'] },
|
|
98
|
+
body: [
|
|
99
|
+
{ predicate: 'edge', terms: ['X', 'Y'] },
|
|
100
|
+
{ predicate: 'path', terms: ['Y', 'Z'] }
|
|
96
101
|
]
|
|
97
|
-
);
|
|
102
|
+
}));
|
|
98
103
|
|
|
99
104
|
console.log('Rules added:');
|
|
100
105
|
console.log(' path(X, Y) :- edge(X, Y) % base case');
|
|
101
106
|
console.log(' path(X, Z) :- edge(X, Y), path(Y, Z) % recursive case\n');
|
|
102
107
|
|
|
103
108
|
// Evaluate using semi-naive algorithm
|
|
104
|
-
const
|
|
105
|
-
console.log(`Semi-naive evaluation completed
|
|
109
|
+
const evalResult = evaluateDatalog(program);
|
|
110
|
+
console.log(`Semi-naive evaluation completed: ${evalResult}\n`);
|
|
106
111
|
|
|
107
112
|
// Query all paths from 'a'
|
|
108
|
-
const paths =
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
const paths = queryDatalog(program, 'path');
|
|
114
|
+
const results = JSON.parse(paths);
|
|
115
|
+
|
|
116
|
+
console.log('Query: All paths');
|
|
117
|
+
console.log('Results (nodes reachable from each source):');
|
|
118
|
+
for (const result of results) {
|
|
119
|
+
console.log(` ${result.terms[0]} can reach ${result.terms[1]}`);
|
|
113
120
|
}
|
|
114
121
|
console.log();
|
|
115
122
|
}
|
|
116
123
|
|
|
117
124
|
// =============================================================================
|
|
118
|
-
// Example 3:
|
|
125
|
+
// Example 3: Fraud Detection Rules
|
|
119
126
|
// =============================================================================
|
|
120
127
|
|
|
121
|
-
|
|
122
|
-
console.log('===
|
|
123
|
-
|
|
124
|
-
const
|
|
128
|
+
function fraudDetectionExample() {
|
|
129
|
+
console.log('=== Fraud Detection Rules ===\n');
|
|
130
|
+
|
|
131
|
+
const program = new DatalogProgram();
|
|
132
|
+
|
|
133
|
+
// Add transaction facts: transaction(id, sender, receiver, amount)
|
|
134
|
+
program.addFact(JSON.stringify({
|
|
135
|
+
predicate: 'transaction',
|
|
136
|
+
terms: ['tx001', 'account_a', 'account_b', '50000']
|
|
137
|
+
}));
|
|
138
|
+
program.addFact(JSON.stringify({
|
|
139
|
+
predicate: 'transaction',
|
|
140
|
+
terms: ['tx002', 'account_b', 'account_c', '48000']
|
|
141
|
+
}));
|
|
142
|
+
program.addFact(JSON.stringify({
|
|
143
|
+
predicate: 'transaction',
|
|
144
|
+
terms: ['tx003', 'account_c', 'account_a', '45000']
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
// Add account facts: account(id, jurisdiction)
|
|
148
|
+
program.addFact(JSON.stringify({ predicate: 'account', terms: ['account_a', 'Panama'] }));
|
|
149
|
+
program.addFact(JSON.stringify({ predicate: 'account', terms: ['account_b', 'BVI'] }));
|
|
150
|
+
program.addFact(JSON.stringify({ predicate: 'account', terms: ['account_c', 'Cayman'] }));
|
|
151
|
+
|
|
152
|
+
// Add high-risk jurisdiction facts
|
|
153
|
+
program.addFact(JSON.stringify({ predicate: 'high_risk_jurisdiction', terms: ['Panama'] }));
|
|
154
|
+
program.addFact(JSON.stringify({ predicate: 'high_risk_jurisdiction', terms: ['BVI'] }));
|
|
155
|
+
program.addFact(JSON.stringify({ predicate: 'high_risk_jurisdiction', terms: ['Cayman'] }));
|
|
156
|
+
|
|
157
|
+
console.log('Facts loaded:');
|
|
158
|
+
console.log(' 3 transactions forming circular pattern');
|
|
159
|
+
console.log(' 3 accounts in offshore jurisdictions\n');
|
|
160
|
+
|
|
161
|
+
// Rule: sends_to(A, B) :- transaction(_, A, B, _)
|
|
162
|
+
program.addRule(JSON.stringify({
|
|
163
|
+
head: { predicate: 'sends_to', terms: ['A', 'B'] },
|
|
164
|
+
body: [{ predicate: 'transaction', terms: ['_', 'A', 'B', '_'] }]
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
// Rule: circular_flow(A, B, C) :- sends_to(A, B), sends_to(B, C), sends_to(C, A)
|
|
168
|
+
program.addRule(JSON.stringify({
|
|
169
|
+
head: { predicate: 'circular_flow', terms: ['A', 'B', 'C'] },
|
|
170
|
+
body: [
|
|
171
|
+
{ predicate: 'sends_to', terms: ['A', 'B'] },
|
|
172
|
+
{ predicate: 'sends_to', terms: ['B', 'C'] },
|
|
173
|
+
{ predicate: 'sends_to', terms: ['C', 'A'] }
|
|
174
|
+
]
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
// Rule: offshore_account(A) :- account(A, J), high_risk_jurisdiction(J)
|
|
178
|
+
program.addRule(JSON.stringify({
|
|
179
|
+
head: { predicate: 'offshore_account', terms: ['A'] },
|
|
180
|
+
body: [
|
|
181
|
+
{ predicate: 'account', terms: ['A', 'J'] },
|
|
182
|
+
{ predicate: 'high_risk_jurisdiction', terms: ['J'] }
|
|
183
|
+
]
|
|
184
|
+
}));
|
|
185
|
+
|
|
186
|
+
console.log('Fraud detection rules:');
|
|
187
|
+
console.log(' sends_to(A, B) :- transaction(_, A, B, _)');
|
|
188
|
+
console.log(' circular_flow(A, B, C) :- sends_to(A, B), sends_to(B, C), sends_to(C, A)');
|
|
189
|
+
console.log(' offshore_account(A) :- account(A, J), high_risk_jurisdiction(J)\n');
|
|
190
|
+
|
|
191
|
+
// Evaluate
|
|
192
|
+
evaluateDatalog(program);
|
|
193
|
+
|
|
194
|
+
// Query circular flows
|
|
195
|
+
const circularFlows = queryDatalog(program, 'circular_flow');
|
|
196
|
+
const circularResults = JSON.parse(circularFlows);
|
|
197
|
+
console.log('Circular payment patterns detected:');
|
|
198
|
+
for (const result of circularResults) {
|
|
199
|
+
console.log(` ${result.terms[0]} -> ${result.terms[1]} -> ${result.terms[2]} -> ${result.terms[0]}`);
|
|
200
|
+
}
|
|
125
201
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
202
|
+
// Query offshore accounts
|
|
203
|
+
const offshoreAccounts = queryDatalog(program, 'offshore_account');
|
|
204
|
+
const offshoreResults = JSON.parse(offshoreAccounts);
|
|
205
|
+
console.log('\nOffshore accounts flagged:');
|
|
206
|
+
for (const result of offshoreResults) {
|
|
207
|
+
console.log(` ${result.terms[0]}`);
|
|
208
|
+
}
|
|
209
|
+
console.log();
|
|
210
|
+
}
|
|
131
211
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Example 4: Access Control Rules
|
|
214
|
+
// =============================================================================
|
|
135
215
|
|
|
136
|
-
|
|
137
|
-
console.log('
|
|
138
|
-
console.log(' employee(carol, sales), employee(david, hr)');
|
|
139
|
-
console.log(' manager(alice), manager(carol)\n');
|
|
216
|
+
function accessControlExample() {
|
|
217
|
+
console.log('=== Access Control Rules ===\n');
|
|
140
218
|
|
|
141
|
-
|
|
142
|
-
engine.addRuleWithNegation(
|
|
143
|
-
'non_manager',
|
|
144
|
-
['X'],
|
|
145
|
-
[{ predicate: 'employee', args: ['X', '_'] }],
|
|
146
|
-
[{ predicate: 'manager', args: ['X'] }] // negated atoms
|
|
147
|
-
);
|
|
219
|
+
const program = new DatalogProgram();
|
|
148
220
|
|
|
149
|
-
|
|
150
|
-
|
|
221
|
+
// User roles
|
|
222
|
+
program.addFact(JSON.stringify({ predicate: 'role', terms: ['alice', 'admin'] }));
|
|
223
|
+
program.addFact(JSON.stringify({ predicate: 'role', terms: ['bob', 'developer'] }));
|
|
224
|
+
program.addFact(JSON.stringify({ predicate: 'role', terms: ['carol', 'developer'] }));
|
|
225
|
+
program.addFact(JSON.stringify({ predicate: 'role', terms: ['david', 'viewer'] }));
|
|
151
226
|
|
|
152
|
-
//
|
|
153
|
-
|
|
227
|
+
// Resource permissions by role
|
|
228
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['admin', 'read'] }));
|
|
229
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['admin', 'write'] }));
|
|
230
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['admin', 'delete'] }));
|
|
231
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['developer', 'read'] }));
|
|
232
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['developer', 'write'] }));
|
|
233
|
+
program.addFact(JSON.stringify({ predicate: 'permission', terms: ['viewer', 'read'] }));
|
|
154
234
|
|
|
155
|
-
|
|
156
|
-
const nonManagers = engine.query('non_manager', ['?person']);
|
|
157
|
-
console.log('Query: non_manager(?person)');
|
|
158
|
-
console.log('Results:');
|
|
159
|
-
for (const result of nonManagers) {
|
|
160
|
-
console.log(` ${result.person} is not a manager`);
|
|
161
|
-
}
|
|
162
|
-
console.log();
|
|
163
|
-
}
|
|
235
|
+
console.log('Access control facts loaded\n');
|
|
164
236
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
237
|
+
// Rule: can_do(User, Action) :- role(User, Role), permission(Role, Action)
|
|
238
|
+
program.addRule(JSON.stringify({
|
|
239
|
+
head: { predicate: 'can_do', terms: ['User', 'Action'] },
|
|
240
|
+
body: [
|
|
241
|
+
{ predicate: 'role', terms: ['User', 'Role'] },
|
|
242
|
+
{ predicate: 'permission', terms: ['Role', 'Action'] }
|
|
243
|
+
]
|
|
244
|
+
}));
|
|
168
245
|
|
|
169
|
-
|
|
170
|
-
console.log('
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
['Product', 'Total'],
|
|
188
|
-
{ predicate: 'sale', args: ['Product', 'Amount', '_'] },
|
|
189
|
-
{ function: 'sum', variable: 'Amount', output: 'Total' },
|
|
190
|
-
['Product'] // group by
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
// Rule: avg_by_region(R, avg(A)) :- sale(_, A, R)
|
|
194
|
-
engine.addAggregateRule(
|
|
195
|
-
'avg_by_region',
|
|
196
|
-
['Region', 'Average'],
|
|
197
|
-
{ predicate: 'sale', args: ['_', 'Amount', 'Region'] },
|
|
198
|
-
{ function: 'avg', variable: 'Amount', output: 'Average' },
|
|
199
|
-
['Region']
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
console.log('Aggregate rules added:');
|
|
203
|
-
console.log(' total_by_product(P, sum(A)) :- sale(P, A, _)');
|
|
204
|
-
console.log(' avg_by_region(R, avg(A)) :- sale(_, A, R)\n');
|
|
205
|
-
|
|
206
|
-
engine.evaluate();
|
|
207
|
-
|
|
208
|
-
// Query totals by product
|
|
209
|
-
const totals = engine.query('total_by_product', ['?product', '?total']);
|
|
210
|
-
console.log('Total sales by product:');
|
|
211
|
-
for (const result of totals) {
|
|
212
|
-
console.log(` ${result.product}: $${result.total}`);
|
|
246
|
+
console.log('Access control rules:');
|
|
247
|
+
console.log(' can_do(User, Action) :- role(User, Role), permission(Role, Action)\n');
|
|
248
|
+
|
|
249
|
+
evaluateDatalog(program);
|
|
250
|
+
|
|
251
|
+
// Query all permissions
|
|
252
|
+
const permissions = queryDatalog(program, 'can_do');
|
|
253
|
+
const results = JSON.parse(permissions);
|
|
254
|
+
|
|
255
|
+
console.log('Derived permissions:');
|
|
256
|
+
const permissionsByUser: { [key: string]: string[] } = {};
|
|
257
|
+
for (const result of results) {
|
|
258
|
+
const user = result.terms[0];
|
|
259
|
+
const action = result.terms[1];
|
|
260
|
+
if (!permissionsByUser[user]) {
|
|
261
|
+
permissionsByUser[user] = [];
|
|
262
|
+
}
|
|
263
|
+
permissionsByUser[user].push(action);
|
|
213
264
|
}
|
|
214
265
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
console.log('\nAverage sale by region:');
|
|
218
|
-
for (const result of avgs) {
|
|
219
|
-
console.log(` ${result.region}: $${result.avg}`);
|
|
266
|
+
for (const [user, actions] of Object.entries(permissionsByUser)) {
|
|
267
|
+
console.log(` ${user} can: ${actions.join(', ')}`);
|
|
220
268
|
}
|
|
221
269
|
console.log();
|
|
222
270
|
}
|
|
223
271
|
|
|
224
272
|
// =============================================================================
|
|
225
|
-
// Example 5: RDF Graph
|
|
273
|
+
// Example 5: Integration with RDF Graph
|
|
226
274
|
// =============================================================================
|
|
227
275
|
|
|
228
|
-
|
|
229
|
-
console.log('=== RDF Graph Integration ===\n');
|
|
276
|
+
function rdfIntegrationExample() {
|
|
277
|
+
console.log('=== RDF Graph + Datalog Integration ===\n');
|
|
230
278
|
|
|
231
279
|
// Create RDF graph
|
|
232
|
-
const db = new
|
|
280
|
+
const db = new GraphDB('http://example.org/company');
|
|
233
281
|
|
|
234
|
-
// Load company data
|
|
282
|
+
// Load company data in Turtle format
|
|
235
283
|
const ttl = `
|
|
236
284
|
@prefix ex: <http://example.org/> .
|
|
237
285
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
@@ -258,193 +306,52 @@ async function rdfIntegrationExample() {
|
|
|
258
306
|
`;
|
|
259
307
|
|
|
260
308
|
db.loadTtl(ttl, null);
|
|
261
|
-
console.log(
|
|
262
|
-
|
|
263
|
-
//
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
// Add reasoning rules
|
|
271
|
-
// colleague(X, Y) :- memberOf(X, Dept), memberOf(Y, Dept), X != Y
|
|
272
|
-
engine.addRule(
|
|
273
|
-
'colleague',
|
|
274
|
-
['X', 'Y'],
|
|
275
|
-
[
|
|
276
|
-
{ predicate: 'triple', args: ['X', 'http://www.w3.org/ns/org#memberOf', 'Dept'] },
|
|
277
|
-
{ predicate: 'triple', args: ['Y', 'http://www.w3.org/ns/org#memberOf', 'Dept'] }
|
|
278
|
-
],
|
|
279
|
-
{ filter: 'X != Y' }
|
|
280
|
-
);
|
|
281
|
-
|
|
282
|
-
// manager(X, Y) :- reportsTo(Y, X)
|
|
283
|
-
engine.addRule(
|
|
284
|
-
'manager',
|
|
285
|
-
['Manager', 'Employee'],
|
|
286
|
-
[
|
|
287
|
-
{ predicate: 'triple', args: ['Employee', 'http://www.w3.org/ns/org#reportsTo', 'Manager'] }
|
|
288
|
-
]
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
console.log('Reasoning rules added:');
|
|
292
|
-
console.log(' colleague(X, Y) :- memberOf(X, Dept), memberOf(Y, Dept), X != Y');
|
|
293
|
-
console.log(' manager(M, E) :- reportsTo(E, M)\n');
|
|
309
|
+
console.log(`RDF graph loaded with ${db.countTriples()} triples\n`);
|
|
310
|
+
|
|
311
|
+
// Query to extract facts for Datalog
|
|
312
|
+
const membershipQuery = `
|
|
313
|
+
PREFIX org: <http://www.w3.org/ns/org#>
|
|
314
|
+
SELECT ?person ?dept WHERE {
|
|
315
|
+
?person org:memberOf ?dept .
|
|
316
|
+
}
|
|
317
|
+
`;
|
|
294
318
|
|
|
295
|
-
|
|
319
|
+
const memberships = db.querySelect(membershipQuery);
|
|
296
320
|
|
|
297
|
-
//
|
|
298
|
-
const
|
|
299
|
-
console.log('Colleagues (same department):');
|
|
300
|
-
for (const result of colleagues) {
|
|
301
|
-
console.log(` ${result.person1.split('/').pop()} and ${result.person2.split('/').pop()}`);
|
|
302
|
-
}
|
|
321
|
+
// Create Datalog program from SPARQL results
|
|
322
|
+
const program = new DatalogProgram();
|
|
303
323
|
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
324
|
+
console.log('Converting RDF to Datalog facts:');
|
|
325
|
+
for (const row of memberships) {
|
|
326
|
+
const person = row.bindings.person.split('/').pop() || '';
|
|
327
|
+
const dept = row.bindings.dept.split('/').pop() || '';
|
|
328
|
+
program.addFact(JSON.stringify({ predicate: 'member_of', terms: [person, dept] }));
|
|
329
|
+
console.log(` member_of(${person}, ${dept})`);
|
|
309
330
|
}
|
|
310
331
|
console.log();
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// =============================================================================
|
|
314
|
-
// Example 6: Access Control Rules (Practical Application)
|
|
315
|
-
// =============================================================================
|
|
316
|
-
|
|
317
|
-
async function accessControlExample() {
|
|
318
|
-
console.log('=== Access Control Rules ===\n');
|
|
319
|
-
|
|
320
|
-
const engine = new DatalogEngine();
|
|
321
|
-
|
|
322
|
-
// User roles
|
|
323
|
-
engine.addFact('role', ['alice', 'admin']);
|
|
324
|
-
engine.addFact('role', ['bob', 'developer']);
|
|
325
|
-
engine.addFact('role', ['carol', 'developer']);
|
|
326
|
-
engine.addFact('role', ['david', 'viewer']);
|
|
327
|
-
|
|
328
|
-
// Resource permissions by role
|
|
329
|
-
engine.addFact('permission', ['admin', 'read']);
|
|
330
|
-
engine.addFact('permission', ['admin', 'write']);
|
|
331
|
-
engine.addFact('permission', ['admin', 'delete']);
|
|
332
|
-
engine.addFact('permission', ['developer', 'read']);
|
|
333
|
-
engine.addFact('permission', ['developer', 'write']);
|
|
334
|
-
engine.addFact('permission', ['viewer', 'read']);
|
|
335
|
-
|
|
336
|
-
// Resource ownership
|
|
337
|
-
engine.addFact('owns', ['alice', 'resource1']);
|
|
338
|
-
engine.addFact('owns', ['bob', 'resource2']);
|
|
339
332
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
[
|
|
347
|
-
{ predicate: 'role', args: ['User', 'Role'] },
|
|
348
|
-
{ predicate: 'permission', args: ['Role', 'Action'] }
|
|
333
|
+
// Add rule: colleague(X, Y) :- member_of(X, Dept), member_of(Y, Dept)
|
|
334
|
+
program.addRule(JSON.stringify({
|
|
335
|
+
head: { predicate: 'colleague', terms: ['X', 'Y'] },
|
|
336
|
+
body: [
|
|
337
|
+
{ predicate: 'member_of', terms: ['X', 'Dept'] },
|
|
338
|
+
{ predicate: 'member_of', terms: ['Y', 'Dept'] }
|
|
349
339
|
]
|
|
350
|
-
);
|
|
351
|
-
|
|
352
|
-
// Rule: owner_override(User, Resource, delete) :- owns(User, Resource)
|
|
353
|
-
engine.addRule(
|
|
354
|
-
'can_access',
|
|
355
|
-
['User', 'Resource', 'delete'],
|
|
356
|
-
[
|
|
357
|
-
{ predicate: 'owns', args: ['User', 'Resource'] }
|
|
358
|
-
]
|
|
359
|
-
);
|
|
340
|
+
}));
|
|
360
341
|
|
|
361
|
-
console.log('
|
|
362
|
-
console.log(' can_access(U, R, A) :- role(U, Role), permission(Role, A)');
|
|
363
|
-
console.log(' can_access(U, R, delete) :- owns(U, R) % owner override\n');
|
|
364
|
-
|
|
365
|
-
engine.evaluate();
|
|
366
|
-
|
|
367
|
-
// Check specific access
|
|
368
|
-
const checkAccess = (user: string, action: string) => {
|
|
369
|
-
const results = engine.query('can_access', [user, '?resource', action]);
|
|
370
|
-
return results.length > 0;
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const checks = [
|
|
374
|
-
['alice', 'delete'],
|
|
375
|
-
['bob', 'write'],
|
|
376
|
-
['bob', 'delete'],
|
|
377
|
-
['david', 'read'],
|
|
378
|
-
['david', 'write']
|
|
379
|
-
];
|
|
380
|
-
|
|
381
|
-
console.log('Access checks:');
|
|
382
|
-
for (const [user, action] of checks) {
|
|
383
|
-
const allowed = checkAccess(user, action);
|
|
384
|
-
const icon = allowed ? '✓' : '✗';
|
|
385
|
-
console.log(` ${icon} ${user} can ${action}: ${allowed}`);
|
|
386
|
-
}
|
|
342
|
+
console.log('Reasoning rule: colleague(X, Y) :- member_of(X, Dept), member_of(Y, Dept)\n');
|
|
387
343
|
|
|
388
|
-
|
|
389
|
-
const bobCanDelete = engine.query('can_access', ['bob', 'resource2', 'delete']);
|
|
390
|
-
console.log(`\n Bob can delete resource2 (owner): ${bobCanDelete.length > 0}`);
|
|
391
|
-
console.log();
|
|
392
|
-
}
|
|
344
|
+
evaluateDatalog(program);
|
|
393
345
|
|
|
394
|
-
//
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
// Initial facts
|
|
404
|
-
engine.addFact('friend', ['alice', 'bob']);
|
|
405
|
-
engine.addFact('friend', ['bob', 'carol']);
|
|
406
|
-
|
|
407
|
-
// Bidirectional friendship rule
|
|
408
|
-
engine.addRule(
|
|
409
|
-
'friends_with',
|
|
410
|
-
['X', 'Y'],
|
|
411
|
-
[{ predicate: 'friend', args: ['X', 'Y'] }]
|
|
412
|
-
);
|
|
413
|
-
engine.addRule(
|
|
414
|
-
'friends_with',
|
|
415
|
-
['X', 'Y'],
|
|
416
|
-
[{ predicate: 'friend', args: ['Y', 'X'] }]
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
// Transitive friends-of-friends
|
|
420
|
-
engine.addRule(
|
|
421
|
-
'fof',
|
|
422
|
-
['X', 'Z'],
|
|
423
|
-
[
|
|
424
|
-
{ predicate: 'friends_with', args: ['X', 'Y'] },
|
|
425
|
-
{ predicate: 'friends_with', args: ['Y', 'Z'] }
|
|
426
|
-
],
|
|
427
|
-
{ filter: 'X != Z' }
|
|
428
|
-
);
|
|
429
|
-
|
|
430
|
-
engine.evaluate();
|
|
431
|
-
|
|
432
|
-
console.log('Initial state:');
|
|
433
|
-
let fofs = engine.query('fof', ['alice', '?friend']);
|
|
434
|
-
console.log(` Alice's friends-of-friends: ${fofs.map(r => r.friend).join(', ')}\n`);
|
|
435
|
-
|
|
436
|
-
// Incremental update: add new friendship
|
|
437
|
-
console.log('Adding new fact: friend(carol, david)');
|
|
438
|
-
engine.addFact('friend', ['carol', 'david']);
|
|
439
|
-
|
|
440
|
-
// Incremental evaluation (only processes new facts)
|
|
441
|
-
engine.evaluateIncremental();
|
|
442
|
-
|
|
443
|
-
console.log('\nAfter incremental update:');
|
|
444
|
-
fofs = engine.query('fof', ['alice', '?friend']);
|
|
445
|
-
console.log(` Alice's friends-of-friends: ${fofs.map(r => r.friend).join(', ')}`);
|
|
446
|
-
|
|
447
|
-
console.log('\nIncremental evaluation is much faster for dynamic graphs!');
|
|
346
|
+
// Query colleagues
|
|
347
|
+
const colleagues = queryDatalog(program, 'colleague');
|
|
348
|
+
const results = JSON.parse(colleagues);
|
|
349
|
+
console.log('Colleagues (same department):');
|
|
350
|
+
for (const result of results) {
|
|
351
|
+
if (result.terms[0] !== result.terms[1]) { // Exclude self-pairs
|
|
352
|
+
console.log(` ${result.terms[0]} and ${result.terms[1]}`);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
448
355
|
console.log();
|
|
449
356
|
}
|
|
450
357
|
|
|
@@ -452,23 +359,23 @@ async function incrementalExample() {
|
|
|
452
359
|
// Run All Examples
|
|
453
360
|
// =============================================================================
|
|
454
361
|
|
|
455
|
-
|
|
456
|
-
console.log('
|
|
457
|
-
console.log('
|
|
458
|
-
console.log('
|
|
362
|
+
function main() {
|
|
363
|
+
console.log('╔════════════════════════════════════════════════════════════════════╗');
|
|
364
|
+
console.log('║ Datalog Reasoning Examples - rust-kgdb SDK ║');
|
|
365
|
+
console.log('╠════════════════════════════════════════════════════════════════════╣');
|
|
366
|
+
console.log('║ Using DatalogProgram with JSON-based fact/rule API ║');
|
|
367
|
+
console.log('╚════════════════════════════════════════════════════════════════════╝\n');
|
|
459
368
|
|
|
460
369
|
try {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
console.log('
|
|
470
|
-
console.log(' All examples completed successfully!');
|
|
471
|
-
console.log('========================================');
|
|
370
|
+
basicDatalogExample();
|
|
371
|
+
recursiveDatalogExample();
|
|
372
|
+
fraudDetectionExample();
|
|
373
|
+
accessControlExample();
|
|
374
|
+
rdfIntegrationExample();
|
|
375
|
+
|
|
376
|
+
console.log('════════════════════════════════════════════════════════════════════');
|
|
377
|
+
console.log(' All Datalog examples completed successfully!');
|
|
378
|
+
console.log('════════════════════════════════════════════════════════════════════');
|
|
472
379
|
} catch (error) {
|
|
473
380
|
console.error('Error running examples:', error);
|
|
474
381
|
process.exit(1);
|