@unrdf/knowledge-engine 5.0.1 → 26.4.3
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/package.json +13 -7
- package/src/ai-enhanced-search.mjs +371 -0
- package/src/anomaly-detector.mjs +226 -0
- package/src/artifact-generator.mjs +251 -0
- package/src/browser.mjs +1 -1
- package/src/chatman/disruption-arithmetic.mjs +140 -0
- package/src/chatman/market-dynamics.mjs +140 -0
- package/src/chatman/organizational-dynamics.mjs +140 -0
- package/src/chatman/strategic-dynamics.mjs +140 -0
- package/src/chatman-config-loader.mjs +282 -0
- package/src/chatman-engine.mjs +431 -0
- package/src/chatman-operator.mjs +342 -0
- package/src/dark-field-detector.mjs +312 -0
- package/src/formation-theorems.mjs +345 -0
- package/src/index.mjs +20 -2
- package/src/knowledge-hook-manager.mjs +1 -1
- package/src/lockchain-writer-browser.mjs +2 -2
- package/src/observability.mjs +40 -4
- package/src/query-optimizer.mjs +1 -1
- package/src/resolution-layer.mjs +1 -1
- package/src/transaction.mjs +11 -9
- package/README.md +0 -84
- package/src/browser-shims.mjs +0 -343
- package/src/canonicalize.mjs +0 -414
- package/src/condition-cache.mjs +0 -109
- package/src/condition-evaluator.mjs +0 -722
- package/src/dark-matter-core.mjs +0 -742
- package/src/define-hook.mjs +0 -213
- package/src/effect-sandbox-browser.mjs +0 -283
- package/src/effect-sandbox-worker.mjs +0 -170
- package/src/effect-sandbox.mjs +0 -517
- package/src/engines/index.mjs +0 -11
- package/src/engines/rdf-engine.mjs +0 -299
- package/src/file-resolver.mjs +0 -387
- package/src/hook-executor-batching.mjs +0 -277
- package/src/hook-executor.mjs +0 -870
- package/src/hook-management.mjs +0 -150
- package/src/ken-parliment.mjs +0 -119
- package/src/ken.mjs +0 -149
- package/src/knowledge-engine/builtin-rules.mjs +0 -190
- package/src/knowledge-engine/inference-engine.mjs +0 -418
- package/src/knowledge-engine/knowledge-engine.mjs +0 -317
- package/src/knowledge-engine/pattern-dsl.mjs +0 -142
- package/src/knowledge-engine/pattern-matcher.mjs +0 -215
- package/src/knowledge-engine/rules.mjs +0 -184
- package/src/knowledge-engine.mjs +0 -319
- package/src/knowledge-hook-engine.mjs +0 -360
- package/src/knowledge-substrate-core.mjs +0 -927
- package/src/lite.mjs +0 -222
- package/src/lockchain-writer.mjs +0 -602
- package/src/monitoring/andon-signals.mjs +0 -775
- package/src/parse.mjs +0 -290
- package/src/performance-optimizer.mjs +0 -678
- package/src/policy-pack.mjs +0 -572
- package/src/query-cache.mjs +0 -116
- package/src/query.mjs +0 -306
- package/src/reason.mjs +0 -350
- package/src/schemas.mjs +0 -1063
- package/src/security/error-sanitizer.mjs +0 -257
- package/src/security/path-validator.mjs +0 -194
- package/src/security/sandbox-restrictions.mjs +0 -331
- package/src/security-validator.mjs +0 -389
- package/src/store-cache.mjs +0 -137
- package/src/telemetry.mjs +0 -167
- package/src/utils/adaptive-monitor.mjs +0 -746
- package/src/utils/circuit-breaker.mjs +0 -513
- package/src/utils/edge-case-handler.mjs +0 -503
- package/src/utils/memory-manager.mjs +0 -498
- package/src/utils/ring-buffer.mjs +0 -282
- package/src/validate.mjs +0 -319
- package/src/validators/index.mjs +0 -338
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Knowledge Engine Class
|
|
3
|
-
* @module @unrdf/knowledge-engine/knowledge-engine
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { z } from 'zod';
|
|
7
|
-
import { UnrdfDataFactory as DataFactory } from '@unrdf/core/rdf/n3-justified-only';
|
|
8
|
-
import { getBuiltinRules } from './builtin-rules.mjs';
|
|
9
|
-
|
|
10
|
-
const { namedNode } = DataFactory;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* @typedef {Object} KnowledgeEngineOptions
|
|
14
|
-
* @property {import('n3').Store} store - N3 Store for RDF data
|
|
15
|
-
* @property {Array<Rule>} [rules] - Additional custom rules
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* @typedef {Object} InferenceStats
|
|
20
|
-
* @property {number} triplesInferred - Number of triples inferred
|
|
21
|
-
* @property {number} iterations - Number of inference iterations
|
|
22
|
-
* @property {number} duration - Duration in milliseconds
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
// Namespace prefixes
|
|
26
|
-
const NAMESPACES = {
|
|
27
|
-
'rdf:': 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
|
|
28
|
-
'rdfs:': 'http://www.w3.org/2000/01/rdf-schema#',
|
|
29
|
-
'owl:': 'http://www.w3.org/2002/07/owl#',
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Knowledge Engine - Rule-based inference for RDF graphs
|
|
34
|
-
*/
|
|
35
|
-
export class KnowledgeEngine {
|
|
36
|
-
/**
|
|
37
|
-
* Create a new Knowledge Engine
|
|
38
|
-
* @param {KnowledgeEngineOptions} options
|
|
39
|
-
*/
|
|
40
|
-
constructor(options) {
|
|
41
|
-
const schema = z.object({
|
|
42
|
-
store: z.any(),
|
|
43
|
-
rules: z.array(z.any()).optional(),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const validated = schema.parse(options);
|
|
47
|
-
|
|
48
|
-
// Validate store has required methods
|
|
49
|
-
if (
|
|
50
|
-
!validated.store ||
|
|
51
|
-
typeof validated.store.addQuad !== 'function' ||
|
|
52
|
-
typeof validated.store.getQuads !== 'function'
|
|
53
|
-
) {
|
|
54
|
-
throw new Error('Store must have addQuad and getQuads methods');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
this.store = validated.store;
|
|
58
|
-
this.rules = validated.rules || getBuiltinRules();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Expand prefixed URIs to full URIs
|
|
63
|
-
* @param {string} value - Prefixed or full URI
|
|
64
|
-
* @returns {string} Full URI
|
|
65
|
-
*/
|
|
66
|
-
expandPrefix(value) {
|
|
67
|
-
for (const [prefix, namespace] of Object.entries(NAMESPACES)) {
|
|
68
|
-
if (value.startsWith(prefix)) {
|
|
69
|
-
return value.replace(prefix, namespace);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return value;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Run inference and materialize inferred triples into the store
|
|
77
|
-
* @returns {InferenceStats} Inference statistics
|
|
78
|
-
*/
|
|
79
|
-
materialize() {
|
|
80
|
-
const startTime = Date.now();
|
|
81
|
-
let triplesInferred = 0;
|
|
82
|
-
let iterations = 0;
|
|
83
|
-
const maxIterations = 100;
|
|
84
|
-
|
|
85
|
-
let changed = true;
|
|
86
|
-
while (changed && iterations < maxIterations) {
|
|
87
|
-
changed = false;
|
|
88
|
-
iterations++;
|
|
89
|
-
|
|
90
|
-
const beforeCount = this.store.size;
|
|
91
|
-
|
|
92
|
-
for (const rule of this.rules) {
|
|
93
|
-
const newTriples = this.applyRule(rule);
|
|
94
|
-
triplesInferred += newTriples;
|
|
95
|
-
if (newTriples > 0) {
|
|
96
|
-
changed = true;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const afterCount = this.store.size;
|
|
101
|
-
const actualNew = afterCount - beforeCount;
|
|
102
|
-
|
|
103
|
-
if (actualNew === 0) {
|
|
104
|
-
changed = false;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const duration = Date.now() - startTime;
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
triplesInferred,
|
|
112
|
-
iterations,
|
|
113
|
-
duration,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Apply a single rule to the store
|
|
119
|
-
* @param {Object} rule - Rule to apply
|
|
120
|
-
* @returns {number} Number of new triples inferred
|
|
121
|
-
*/
|
|
122
|
-
applyRule(rule) {
|
|
123
|
-
let inferredCount = 0;
|
|
124
|
-
const patterns = Array.isArray(rule.pattern) ? rule.pattern : [rule.pattern];
|
|
125
|
-
const consequents = Array.isArray(rule.consequent) ? rule.consequent : [rule.consequent];
|
|
126
|
-
|
|
127
|
-
// Find all binding sets that match the pattern
|
|
128
|
-
const bindingSets = this.findBindings(patterns);
|
|
129
|
-
|
|
130
|
-
// For each binding set, generate consequent triples
|
|
131
|
-
for (const bindings of bindingSets) {
|
|
132
|
-
for (const consequent of consequents) {
|
|
133
|
-
const triple = this.instantiateConsequent(consequent, bindings);
|
|
134
|
-
if (triple && this.addTriple(triple)) {
|
|
135
|
-
inferredCount++;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return inferredCount;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Find all binding sets that match the patterns
|
|
145
|
-
* @param {Array<Object>} patterns - Patterns to match
|
|
146
|
-
* @returns {Array<Object>} Array of variable bindings
|
|
147
|
-
*/
|
|
148
|
-
findBindings(patterns) {
|
|
149
|
-
if (patterns.length === 0) return [];
|
|
150
|
-
if (patterns.length === 1) {
|
|
151
|
-
return this.matchPattern(patterns[0]);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Match first pattern
|
|
155
|
-
let bindings = this.matchPattern(patterns[0]);
|
|
156
|
-
|
|
157
|
-
// Join with remaining patterns
|
|
158
|
-
for (let i = 1; i < patterns.length; i++) {
|
|
159
|
-
const nextBindings = [];
|
|
160
|
-
for (const binding of bindings) {
|
|
161
|
-
const matches = this.matchPatternWithBindings(patterns[i], binding);
|
|
162
|
-
nextBindings.push(...matches);
|
|
163
|
-
}
|
|
164
|
-
bindings = nextBindings;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return bindings;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Match a single pattern against the store
|
|
172
|
-
* @param {Object} pattern - Pattern to match
|
|
173
|
-
* @returns {Array<Object>} Array of variable bindings
|
|
174
|
-
*/
|
|
175
|
-
matchPattern(pattern) {
|
|
176
|
-
const bindings = [];
|
|
177
|
-
const subj = this.expandPrefix(pattern.subject);
|
|
178
|
-
const pred = this.expandPrefix(pattern.predicate);
|
|
179
|
-
const obj = this.expandPrefix(pattern.object);
|
|
180
|
-
|
|
181
|
-
const subjVar = subj.startsWith('?');
|
|
182
|
-
const predVar = pred.startsWith('?');
|
|
183
|
-
const objVar = obj.startsWith('?');
|
|
184
|
-
|
|
185
|
-
const quads = this.store.getQuads(
|
|
186
|
-
subjVar ? null : namedNode(subj),
|
|
187
|
-
predVar ? null : namedNode(pred),
|
|
188
|
-
objVar ? null : namedNode(obj),
|
|
189
|
-
null
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
for (const quad of quads) {
|
|
193
|
-
const binding = {};
|
|
194
|
-
if (subjVar) binding[subj] = quad.subject;
|
|
195
|
-
if (predVar) binding[pred] = quad.predicate;
|
|
196
|
-
if (objVar) binding[obj] = quad.object;
|
|
197
|
-
bindings.push(binding);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return bindings;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Match a pattern with existing bindings
|
|
205
|
-
* @param {Object} pattern - Pattern to match
|
|
206
|
-
* @param {Object} existingBindings - Existing variable bindings
|
|
207
|
-
* @returns {Array<Object>} Array of extended variable bindings
|
|
208
|
-
*/
|
|
209
|
-
matchPatternWithBindings(pattern, existingBindings) {
|
|
210
|
-
const bindings = [];
|
|
211
|
-
const subj = this.expandPrefix(pattern.subject);
|
|
212
|
-
const pred = this.expandPrefix(pattern.predicate);
|
|
213
|
-
const obj = this.expandPrefix(pattern.object);
|
|
214
|
-
|
|
215
|
-
const subjVal = subj.startsWith('?') ? existingBindings[subj] : namedNode(subj);
|
|
216
|
-
const predVal = pred.startsWith('?') ? existingBindings[pred] : namedNode(pred);
|
|
217
|
-
const objVal = obj.startsWith('?') ? existingBindings[obj] : namedNode(obj);
|
|
218
|
-
|
|
219
|
-
const quads = this.store.getQuads(subjVal || null, predVal || null, objVal || null, null);
|
|
220
|
-
|
|
221
|
-
for (const quad of quads) {
|
|
222
|
-
const binding = { ...existingBindings };
|
|
223
|
-
let consistent = true;
|
|
224
|
-
|
|
225
|
-
if (subj.startsWith('?')) {
|
|
226
|
-
if (existingBindings[subj] && !quad.subject.equals(existingBindings[subj])) {
|
|
227
|
-
consistent = false;
|
|
228
|
-
} else {
|
|
229
|
-
binding[subj] = quad.subject;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (pred.startsWith('?')) {
|
|
234
|
-
if (existingBindings[pred] && !quad.predicate.equals(existingBindings[pred])) {
|
|
235
|
-
consistent = false;
|
|
236
|
-
} else {
|
|
237
|
-
binding[pred] = quad.predicate;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (obj.startsWith('?')) {
|
|
242
|
-
if (existingBindings[obj] && !quad.object.equals(existingBindings[obj])) {
|
|
243
|
-
consistent = false;
|
|
244
|
-
} else {
|
|
245
|
-
binding[obj] = quad.object;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (consistent) {
|
|
250
|
-
bindings.push(binding);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return bindings;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Instantiate a consequent with variable bindings
|
|
259
|
-
* @param {Object} consequent - Consequent pattern
|
|
260
|
-
* @param {Object} bindings - Variable bindings
|
|
261
|
-
* @returns {Object|null} Instantiated triple or null
|
|
262
|
-
*/
|
|
263
|
-
instantiateConsequent(consequent, bindings) {
|
|
264
|
-
const subj = this.expandPrefix(consequent.subject);
|
|
265
|
-
const pred = this.expandPrefix(consequent.predicate);
|
|
266
|
-
const obj = this.expandPrefix(consequent.object);
|
|
267
|
-
|
|
268
|
-
const subjTerm = subj.startsWith('?') ? bindings[subj] : namedNode(subj);
|
|
269
|
-
const predTerm = pred.startsWith('?') ? bindings[pred] : namedNode(pred);
|
|
270
|
-
const objTerm = obj.startsWith('?') ? bindings[obj] : namedNode(obj);
|
|
271
|
-
|
|
272
|
-
if (!subjTerm || !predTerm || !objTerm) {
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
return { subject: subjTerm, predicate: predTerm, object: objTerm };
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Add a triple to the store if it doesn't already exist
|
|
281
|
-
* @param {Object} triple - Triple to add
|
|
282
|
-
* @returns {boolean} True if added, false if already exists
|
|
283
|
-
*/
|
|
284
|
-
addTriple(triple) {
|
|
285
|
-
const existing = this.store.getQuads(triple.subject, triple.predicate, triple.object, null);
|
|
286
|
-
if (existing.length > 0) {
|
|
287
|
-
return false;
|
|
288
|
-
}
|
|
289
|
-
this.store.addQuad(triple.subject, triple.predicate, triple.object);
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Add a custom rule to the engine
|
|
295
|
-
* @param {Rule} rule - Rule to add
|
|
296
|
-
*/
|
|
297
|
-
addRule(rule) {
|
|
298
|
-
this.rules.push(rule);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Get all rules in the engine
|
|
303
|
-
* @returns {Array<Rule>} All rules
|
|
304
|
-
*/
|
|
305
|
-
getRules() {
|
|
306
|
-
return this.rules;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Clear all inferred triples from the store
|
|
311
|
-
*/
|
|
312
|
-
clear() {
|
|
313
|
-
// Note: This would need additional tracking to distinguish
|
|
314
|
-
// inferred triples from asserted triples
|
|
315
|
-
// For now, this is a no-op
|
|
316
|
-
}
|
|
317
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Pattern DSL - Simple pattern parsing
|
|
3
|
-
* @module @unrdf/knowledge-engine/pattern-dsl
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Parse a simple pattern string
|
|
8
|
-
* Format: "?subject predicate ?object"
|
|
9
|
-
*
|
|
10
|
-
* @param {string} patternString - Pattern string to parse
|
|
11
|
-
* @returns {Object} Parsed pattern object
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const pattern = parsePattern("?person rdf:type foaf:Person");
|
|
15
|
-
* // Returns: { subject: '?person', predicate: 'rdf:type', object: 'foaf:Person' }
|
|
16
|
-
*/
|
|
17
|
-
export function parsePattern(patternString) {
|
|
18
|
-
const trimmed = patternString.trim();
|
|
19
|
-
const parts = trimmed.split(/\s+/);
|
|
20
|
-
|
|
21
|
-
if (parts.length < 3) {
|
|
22
|
-
throw new Error(
|
|
23
|
-
`Invalid pattern: "${patternString}". Expected format: "subject predicate object"`
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
subject: parts[0],
|
|
29
|
-
predicate: parts[1],
|
|
30
|
-
object: parts.slice(2).join(' '),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Convert a pattern to SPARQL WHERE clause
|
|
36
|
-
*
|
|
37
|
-
* @param {Object} pattern - Pattern object
|
|
38
|
-
* @returns {string} SPARQL pattern string
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* const sparql = patternToSparql({ subject: '?x', predicate: 'rdf:type', object: 'foaf:Person' });
|
|
42
|
-
* // Returns: "?x rdf:type foaf:Person ."
|
|
43
|
-
*/
|
|
44
|
-
export function patternToSparql(pattern) {
|
|
45
|
-
const subject = formatTerm(pattern.subject);
|
|
46
|
-
const predicate = formatTerm(pattern.predicate);
|
|
47
|
-
const object = formatTerm(pattern.object);
|
|
48
|
-
|
|
49
|
-
return `${subject} ${predicate} ${object} .`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Format a term for SPARQL
|
|
54
|
-
*
|
|
55
|
-
* @param {string|Object} term - Term to format
|
|
56
|
-
* @returns {string} Formatted term
|
|
57
|
-
*/
|
|
58
|
-
function formatTerm(term) {
|
|
59
|
-
if (!term) return '?_';
|
|
60
|
-
|
|
61
|
-
if (typeof term === 'object' && term.value) {
|
|
62
|
-
return formatTerm(term.value);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const str = String(term);
|
|
66
|
-
|
|
67
|
-
if (str.startsWith('?')) {
|
|
68
|
-
return str;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (str.startsWith('http://') || str.startsWith('https://')) {
|
|
72
|
-
return `<${str}>`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (str.includes(':')) {
|
|
76
|
-
return str;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return `"${str}"`;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Parse multiple patterns from a multi-line string
|
|
84
|
-
*
|
|
85
|
-
* @param {string} patternsString - Multi-line pattern string
|
|
86
|
-
* @returns {Object[]} Array of parsed patterns
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* const patterns = parsePatterns(`
|
|
90
|
-
* ?x rdf:type foaf:Person
|
|
91
|
-
* ?x foaf:name ?name
|
|
92
|
-
* `);
|
|
93
|
-
*/
|
|
94
|
-
export function parsePatterns(patternsString) {
|
|
95
|
-
const lines = patternsString
|
|
96
|
-
.split('\n')
|
|
97
|
-
.map(line => line.trim())
|
|
98
|
-
.filter(line => line && !line.startsWith('#'));
|
|
99
|
-
|
|
100
|
-
return lines.map(parsePattern);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Build a pattern object from components
|
|
105
|
-
*
|
|
106
|
-
* @param {string} subject - Subject term
|
|
107
|
-
* @param {string} predicate - Predicate term
|
|
108
|
-
* @param {string} object - Object term
|
|
109
|
-
* @param {string} [graph] - Optional graph term
|
|
110
|
-
* @returns {Object} Pattern object
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* const pattern = buildPattern('?x', 'rdf:type', 'foaf:Person');
|
|
114
|
-
*/
|
|
115
|
-
export function buildPattern(subject, predicate, object, graph = null) {
|
|
116
|
-
const pattern = { subject, predicate, object };
|
|
117
|
-
|
|
118
|
-
if (graph) {
|
|
119
|
-
pattern.graph = graph;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return pattern;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Validate a pattern object
|
|
127
|
-
*
|
|
128
|
-
* @param {Object} pattern - Pattern to validate
|
|
129
|
-
* @returns {boolean} True if valid
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* if (isValidPattern(pattern)) {
|
|
133
|
-
* console.log('Pattern is valid');
|
|
134
|
-
* }
|
|
135
|
-
*/
|
|
136
|
-
export function isValidPattern(pattern) {
|
|
137
|
-
if (!pattern || typeof pattern !== 'object') {
|
|
138
|
-
return false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return Boolean(pattern.subject && pattern.predicate && pattern.object);
|
|
142
|
-
}
|
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Pattern Matching - Find quads matching triple patterns
|
|
3
|
-
* @module @unrdf/knowledge-engine/pattern-matcher
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { getQuads } from '@unrdf/core';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {import('n3').Quad} Quad
|
|
10
|
-
* @typedef {import('n3').Term} Term
|
|
11
|
-
* @typedef {import('n3').Store} Store
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Match a pattern against the store
|
|
16
|
-
*
|
|
17
|
-
* @param {Store} store - RDF store
|
|
18
|
-
* @param {Object} pattern - Pattern to match
|
|
19
|
-
* @param {string|Object} [pattern.subject] - Subject pattern
|
|
20
|
-
* @param {string|Object} [pattern.predicate] - Predicate pattern
|
|
21
|
-
* @param {string|Object} [pattern.object] - Object pattern
|
|
22
|
-
* @param {string|Object} [pattern.graph] - Graph pattern
|
|
23
|
-
* @returns {Quad[]} Array of matching quads
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* const matches = matchPattern(store, {
|
|
27
|
-
* subject: null,
|
|
28
|
-
* predicate: 'rdf:type',
|
|
29
|
-
* object: 'foaf:Person'
|
|
30
|
-
* });
|
|
31
|
-
*/
|
|
32
|
-
export function matchPattern(store, pattern) {
|
|
33
|
-
const subject = pattern.subject && !isVariable(pattern.subject) ? pattern.subject : null;
|
|
34
|
-
const predicate = pattern.predicate && !isVariable(pattern.predicate) ? pattern.predicate : null;
|
|
35
|
-
const object = pattern.object && !isVariable(pattern.object) ? pattern.object : null;
|
|
36
|
-
const graph = pattern.graph && !isVariable(pattern.graph) ? pattern.graph : null;
|
|
37
|
-
|
|
38
|
-
return getQuads(store, subject, predicate, object, graph);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Match a pattern and return variable bindings
|
|
43
|
-
*
|
|
44
|
-
* @param {Store} store - RDF store
|
|
45
|
-
* @param {Object} pattern - Pattern to match
|
|
46
|
-
* @returns {Object[]} Array of binding objects
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* const bindings = matchPatternWithBindings(store, {
|
|
50
|
-
* subject: '?person',
|
|
51
|
-
* predicate: 'rdf:type',
|
|
52
|
-
* object: 'foaf:Person'
|
|
53
|
-
* });
|
|
54
|
-
* // Returns: [{ person: <http://example.org/alice> }, ...]
|
|
55
|
-
*/
|
|
56
|
-
export function matchPatternWithBindings(store, pattern) {
|
|
57
|
-
const matches = matchPattern(store, pattern);
|
|
58
|
-
const bindings = [];
|
|
59
|
-
|
|
60
|
-
for (const quad of matches) {
|
|
61
|
-
const binding = {};
|
|
62
|
-
|
|
63
|
-
if (isVariable(pattern.subject)) {
|
|
64
|
-
const varName = getVariableName(pattern.subject);
|
|
65
|
-
binding[varName] = quad.subject;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (isVariable(pattern.predicate)) {
|
|
69
|
-
const varName = getVariableName(pattern.predicate);
|
|
70
|
-
binding[varName] = quad.predicate;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (isVariable(pattern.object)) {
|
|
74
|
-
const varName = getVariableName(pattern.object);
|
|
75
|
-
binding[varName] = quad.object;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (pattern.graph && isVariable(pattern.graph)) {
|
|
79
|
-
const varName = getVariableName(pattern.graph);
|
|
80
|
-
binding[varName] = quad.graph;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
bindings.push(binding);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return bindings;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Check if a pattern has at least one match
|
|
91
|
-
*
|
|
92
|
-
* @param {Store} store - RDF store
|
|
93
|
-
* @param {Object} pattern - Pattern to match
|
|
94
|
-
* @returns {boolean} True if pattern matches at least one quad
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* if (hasMatch(store, { subject: '?x', predicate: 'rdf:type', object: 'foaf:Person' })) {
|
|
98
|
-
* console.log('Found at least one person');
|
|
99
|
-
* }
|
|
100
|
-
*/
|
|
101
|
-
export function hasMatch(store, pattern) {
|
|
102
|
-
const matches = matchPattern(store, pattern);
|
|
103
|
-
return matches.length > 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Match multiple patterns (AND conjunction)
|
|
108
|
-
*
|
|
109
|
-
* @param {Store} store - RDF store
|
|
110
|
-
* @param {Object[]} patterns - Array of patterns
|
|
111
|
-
* @returns {Object[]} Array of binding sets that satisfy all patterns
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* const bindings = matchMultiplePatterns(store, [
|
|
115
|
-
* { subject: '?x', predicate: 'rdf:type', object: 'foaf:Person' },
|
|
116
|
-
* { subject: '?x', predicate: 'foaf:name', object: '?name' }
|
|
117
|
-
* ]);
|
|
118
|
-
*/
|
|
119
|
-
export function matchMultiplePatterns(store, patterns) {
|
|
120
|
-
if (patterns.length === 0) {
|
|
121
|
-
return [];
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (patterns.length === 1) {
|
|
125
|
-
return matchPatternWithBindings(store, patterns[0]);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
let currentBindings = matchPatternWithBindings(store, patterns[0]);
|
|
129
|
-
|
|
130
|
-
for (let i = 1; i < patterns.length; i++) {
|
|
131
|
-
const pattern = patterns[i];
|
|
132
|
-
const newBindings = [];
|
|
133
|
-
|
|
134
|
-
for (const binding of currentBindings) {
|
|
135
|
-
const instantiatedPattern = instantiatePattern(pattern, binding);
|
|
136
|
-
const matches = matchPatternWithBindings(store, instantiatedPattern);
|
|
137
|
-
|
|
138
|
-
for (const match of matches) {
|
|
139
|
-
const merged = { ...binding, ...match };
|
|
140
|
-
newBindings.push(merged);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
currentBindings = newBindings;
|
|
145
|
-
|
|
146
|
-
if (currentBindings.length === 0) {
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return currentBindings;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Instantiate a pattern with variable bindings
|
|
156
|
-
*
|
|
157
|
-
* @param {Object} pattern - Pattern template
|
|
158
|
-
* @param {Object} bindings - Variable bindings
|
|
159
|
-
* @returns {Object} Pattern with variables replaced
|
|
160
|
-
*/
|
|
161
|
-
function instantiatePattern(pattern, bindings) {
|
|
162
|
-
return {
|
|
163
|
-
subject: instantiateTerm(pattern.subject, bindings),
|
|
164
|
-
predicate: instantiateTerm(pattern.predicate, bindings),
|
|
165
|
-
object: instantiateTerm(pattern.object, bindings),
|
|
166
|
-
graph: pattern.graph ? instantiateTerm(pattern.graph, bindings) : null,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Instantiate a term with variable bindings
|
|
172
|
-
*
|
|
173
|
-
* @param {string|Object} term - Term or variable
|
|
174
|
-
* @param {Object} bindings - Variable bindings
|
|
175
|
-
* @returns {Object|string} Instantiated term
|
|
176
|
-
*/
|
|
177
|
-
function instantiateTerm(term, bindings) {
|
|
178
|
-
if (!isVariable(term)) {
|
|
179
|
-
return term;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const varName = getVariableName(term);
|
|
183
|
-
return bindings[varName] || term;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Check if a term is a variable
|
|
188
|
-
*
|
|
189
|
-
* @param {string|Object} term - Term to check
|
|
190
|
-
* @returns {boolean} True if term is a variable
|
|
191
|
-
*/
|
|
192
|
-
function isVariable(term) {
|
|
193
|
-
if (!term) return false;
|
|
194
|
-
|
|
195
|
-
if (typeof term === 'object' && term.type === 'variable') {
|
|
196
|
-
return true;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const str = String(term);
|
|
200
|
-
return str.startsWith('?');
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Get variable name (remove '?' prefix)
|
|
205
|
-
*
|
|
206
|
-
* @param {string|Object} term - Variable term
|
|
207
|
-
* @returns {string} Variable name
|
|
208
|
-
*/
|
|
209
|
-
function getVariableName(term) {
|
|
210
|
-
if (typeof term === 'object' && term.value) {
|
|
211
|
-
return term.value.replace(/^\?/, '');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return String(term).replace(/^\?/, '');
|
|
215
|
-
}
|