@comake/skl-js-engine
Version:
Standard Knowledge Language Javascript Engine
402 lines • 16.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.valueToLiteral = exports.triplesToJsonldWithFrame = exports.triplesToJsonld = exports.toJSValueFromDataType = void 0;
/* eslint-disable multiline-comment-style */
/* eslint-disable capitalized-comments */
/* eslint-disable indent */
/* eslint-disable @typescript-eslint/naming-convention */
const data_model_1 = __importDefault(require("@rdfjs/data-model"));
const jsonld = __importStar(require("jsonld"));
const FindOperator_1 = require("../storage/FindOperator");
const Util_1 = require("./Util");
const Vocabularies_1 = require("./Vocabularies");
const BLANK_NODE_PREFIX = '_:';
function toJSValueFromDataType(value, dataType) {
switch (dataType) {
case Vocabularies_1.XSD.int:
case Vocabularies_1.XSD.positiveInteger:
case Vocabularies_1.XSD.negativeInteger:
case Vocabularies_1.XSD.integer: {
return Number.parseInt(value, 10);
}
case Vocabularies_1.XSD.boolean: {
if (value === 'true') {
return true;
}
if (value === 'false') {
return false;
}
return value;
}
case Vocabularies_1.XSD.double:
case Vocabularies_1.XSD.decimal:
case Vocabularies_1.XSD.float: {
return Number.parseFloat(value);
}
case Vocabularies_1.RDF.JSON:
return JSON.parse(value);
default: {
return value;
}
}
}
exports.toJSValueFromDataType = toJSValueFromDataType;
function toJsonLdObject(object) {
if (object.termType === 'Literal') {
if (object.language && object.language.length > 0) {
return {
'@value': object.value,
'@language': object.language
};
}
return {
'@value': toJSValueFromDataType(object.value, object.datatype.value),
'@type': object.datatype.value === Vocabularies_1.RDF.JSON ? '@json' : object.datatype.value
};
}
if (object.termType === 'BlankNode') {
return { '@id': `_:${object.value}` };
}
return { '@id': object.value };
}
function toJsonLdSubject(object) {
if (object.termType === 'BlankNode') {
return `_:${object.value}`;
}
return object.value;
}
function relationsToFrame(relations) {
return Object.entries(relations).reduce((obj, [field, value]) => {
const fieldFrame = {
'@explicit': false
};
let contextAddition;
if (typeof value === 'object' && value.type === 'operator') {
const { resolvedName, relations: subRelations } = value.value;
contextAddition = { [resolvedName]: { '@reverse': field } };
if (subRelations) {
fieldFrame[resolvedName] = relationsToFrame(subRelations);
}
else {
fieldFrame[resolvedName] = {
"@embed": "@always"
};
}
}
else if (typeof value === 'boolean') {
fieldFrame[field] = {
"@embed": "@always"
};
}
else {
fieldFrame[field] = relationsToFrame(value);
}
if (contextAddition) {
return {
...obj,
'@context': {
...obj['@context'],
...contextAddition
},
...fieldFrame
};
}
return {
...obj,
...fieldFrame
};
}, {});
}
function whereToFrame(where) {
if (where.id && typeof where.id === 'string') {
return { '@id': where.id };
}
if (where.id && FindOperator_1.FindOperator.isFindOperator(where.id)) {
const operator = where.id;
if (operator.operator === 'in') {
return { '@id': operator.value };
}
if (operator.operator === 'equal') {
return { '@id': operator.value };
}
}
if (where.type && typeof where.type === 'string') {
return { '@type': where.type };
}
if (where.type && typeof where.type === 'object' && 'value' in where.type && typeof where.type.value === 'string') {
return { '@type': where.type.value };
}
// Handle arbitrary property constraints
/* const frame: NodeObject = {};
Object.entries(where).forEach(([ key, value ]) => {
if (key !== 'id' && key !== 'type') {
if (typeof value === 'string') {
frame[key] = { '@id': value };
} else if (FindOperator.isFindOperator(value)) {
const operator = value as FindOperator<any, any>;
if (operator.operator === 'in') {
frame[key] = { '@id': operator.value };
}
}
}
}); */
return {};
}
function triplesToNodes(triples) {
const nodeIdOrder = [];
const nodesById = triples.reduce((obj, triple) => {
const subject = toJsonLdSubject(triple.subject);
const isTypePredicate = triple.predicate.value === Vocabularies_1.RDF.type;
const predicate = isTypePredicate ? '@type' : triple.predicate.value;
const object = isTypePredicate ? triple.object.value : toJsonLdObject(triple.object);
if (obj[subject]) {
if (obj[subject][predicate]) {
if (Array.isArray(obj[subject][predicate])) {
obj[subject][predicate].push(object);
}
else {
obj[subject][predicate] = [obj[subject][predicate], object];
}
}
else {
obj[subject][predicate] = object;
}
}
else {
obj[subject] = {
'@id': subject,
[predicate]: object
};
if (!subject.startsWith(BLANK_NODE_PREFIX)) {
nodeIdOrder.push(subject);
}
}
return obj;
}, {});
return { nodesById, nodeIdOrder };
}
async function frameWithRelationsOrNonBlankNodes(nodesById, frame, relations, where, queriedType, orderedNodeIds, rdfTypes) {
if (!frame) {
const relationsFrame = relations ? relationsToFrame(relations) : {};
const whereFrame = where ? whereToFrame(where) : {};
frame = { ...relationsFrame, ...whereFrame };
const nodesValue = Object.values(nodesById);
/* // eslint-disable-next-line no-console
console.log('nodesValue', JSON.stringify(nodesValue, null, 2));
// eslint-disable-next-line no-console
console.log('whereFrame', JSON.stringify(whereFrame, null, 2));
// eslint-disable-next-line no-console
console.log('relationsFrame', JSON.stringify(relationsFrame, null, 2)); */
/* Add all the @types in the nodes to the frame */
const typeSet = new Set();
const hasTypeConstraint = whereFrame['@type'];
const hasIriTypeConstraint = where?.id;
if (Object.keys(frame).length === 0) {
// Only add all types when there's no where constraint (no filtering)
nodesValue.forEach((node) => {
const isBlankNode = node['@id']?.startsWith(BLANK_NODE_PREFIX);
if (isBlankNode) {
return;
}
if (node['@type'] && frame && typeof frame === 'object') {
(0, Util_1.ensureArray)(node['@type']).forEach((type) => {
typeSet.add(type);
});
}
});
frame['@type'] ?? (frame['@type'] = []);
frame['@type'] = [...typeSet];
}
else if (hasTypeConstraint) {
// For type-based queries, include the constraint type and types from entities that matched the WHERE clause
const constraintType = hasTypeConstraint;
// Always include the queried/constraint type
typeSet.add(constraintType);
if (rdfTypes && Array.isArray(rdfTypes)) {
rdfTypes.forEach((rdfType) => {
typeSet.add(rdfType);
});
}
// If we have orderedNodeIds (entities that matched the WHERE clause), only include types from those
// This prevents over-matching related entities while still supporting subclass matching
if (orderedNodeIds && orderedNodeIds.length > 0) {
const orderedNodeIdSet = new Set(orderedNodeIds);
nodesValue.forEach((node) => {
const nodeId = node['@id'];
if (!nodeId || node['@id']?.startsWith(BLANK_NODE_PREFIX)) {
return;
}
// Only include types from entities that matched the WHERE clause (in orderedNodeIds)
if (orderedNodeIdSet.has(nodeId) && node['@type'] && frame && typeof frame === 'object') {
const nodeTypes = (0, Util_1.ensureArray)(node['@type']);
nodeTypes.forEach((type) => {
if (type) {
typeSet.add(type);
}
});
}
});
}
else {
// Fallback: if no orderedNodeIds, only add types from entities with the constraint type
nodesValue.forEach((node) => {
const isBlankNode = node['@id']?.startsWith(BLANK_NODE_PREFIX);
if (isBlankNode) {
return;
}
if (node['@type'] && frame && typeof frame === 'object') {
const nodeTypes = (0, Util_1.ensureArray)(node['@type']);
if (nodeTypes.includes(constraintType)) {
nodeTypes.forEach((type) => {
typeSet.add(type);
});
}
}
});
}
frame['@type'] ?? (frame['@type'] = []);
frame['@type'] = [...typeSet];
// When we have orderedNodeIds, restrict root-level results to only those IDs
// This prevents related entities with the same type from appearing at the root level
// Only apply this constraint if we have non-blank node IDs, since blank nodes
// will be filtered out anyway and we need them during framing
if (orderedNodeIds && orderedNodeIds.length > 0) {
const nonBlankNodeIds = orderedNodeIds.filter((id) => !id.startsWith('_:'));
if (nonBlankNodeIds.length > 0) {
frame['@id'] = nonBlankNodeIds;
}
}
}
else if (hasIriTypeConstraint && !frame['@id']) {
frame['@id'] = Object.keys(nodesById).map((nodeId) => {
if (!nodeId.startsWith(BLANK_NODE_PREFIX)) {
return nodeId;
}
return undefined;
}).filter(Boolean);
}
if (Object.keys(frame).length > 0) {
const results = await jsonld.frame({ '@graph': nodesValue }, frame);
if (Array.isArray(results['@graph'])) {
results['@graph'] = results['@graph'].filter((node) => !node['@id']?.startsWith(BLANK_NODE_PREFIX));
}
if (typeof frame === 'object' && '@context' in frame && Object.keys(frame['@context']).length > 0) {
let resultsList;
if (Array.isArray(results)) {
resultsList = results;
}
else if ('@graph' in results) {
resultsList = (0, Util_1.ensureArray)(results['@graph']);
}
else {
const { '@context': unusedContext, ...entityResult } = results;
resultsList = [entityResult];
}
/* return {
'@graph': resultsList.filter((result): boolean =>
Object.keys((frame as NodeObject)['@context']!).some((relationField): boolean => relationField in result))
}; */
return resultsList;
}
return results;
}
const nonBlankNodes = Object.keys(nodesById).filter((nodeId) => !nodeId.startsWith(BLANK_NODE_PREFIX));
return await jsonld.frame({ '@graph': Object.values(nodesById) }, { '@id': nonBlankNodes });
}
return await jsonld.frame({ '@graph': Object.values(nodesById) }, frame);
}
function sortNodesByOrder(nodes, nodeIdOrder) {
return nodes.sort((aNode, bNode) => nodeIdOrder.indexOf(aNode['@id']) - nodeIdOrder.indexOf(bNode['@id']));
}
function sortGraphOfNodeObject(graphObject, nodeIdOrder) {
return {
...graphObject,
'@graph': sortNodesByOrder(graphObject['@graph'], nodeIdOrder)
};
}
async function triplesToJsonld(triples, skipFraming, relations, where, orderedNodeIds, rdfTypes) {
if (triples.length === 0) {
return [];
}
const { nodeIdOrder, nodesById } = triplesToNodes(triples);
if (skipFraming) {
return Object.values(nodesById);
}
// Extract the queried type from where clause for subclass handling
const queriedType = where?.type && typeof where.type === 'string' ? where.type : undefined;
const framed = await frameWithRelationsOrNonBlankNodes(nodesById, undefined, relations, where, queriedType, orderedNodeIds, rdfTypes);
if ('@graph' in framed) {
return sortNodesByOrder(framed['@graph'], orderedNodeIds ?? nodeIdOrder);
}
return framed;
}
exports.triplesToJsonld = triplesToJsonld;
async function triplesToJsonldWithFrame(triples, frame) {
const { nodeIdOrder, nodesById } = triplesToNodes(triples);
const framed = await frameWithRelationsOrNonBlankNodes(nodesById, frame);
if ('@graph' in framed) {
return sortGraphOfNodeObject(framed, nodeIdOrder);
}
const { '@context': context, ...framedWithoutContext } = framed;
const graphObject = {
'@graph': [framedWithoutContext]
};
if (context) {
graphObject['@context'] = context;
}
return graphObject;
}
exports.triplesToJsonldWithFrame = triplesToJsonldWithFrame;
function valueToLiteral(value, datatype) {
if (datatype) {
if (datatype === '@json' || datatype === Vocabularies_1.RDF.JSON) {
return data_model_1.default.literal(JSON.stringify(value), Vocabularies_1.RDF.JSON);
}
return data_model_1.default.literal(value.toString(), datatype);
}
if (typeof value === 'number') {
if (Number.isInteger(value)) {
return data_model_1.default.literal(value.toString(), Vocabularies_1.XSD.integer);
}
return data_model_1.default.literal(value.toString(), Vocabularies_1.XSD.decimal);
}
if (typeof value === 'boolean') {
return data_model_1.default.literal(value.toString(), Vocabularies_1.XSD.boolean);
}
if (typeof value === 'string' && value.startsWith('?') && value.length > 1) {
return data_model_1.default.variable(value.slice(1));
}
if (value instanceof Date) {
return data_model_1.default.literal(value.toISOString(), Vocabularies_1.XSD.dateTime);
}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return data_model_1.default.literal(value.toString());
}
exports.valueToLiteral = valueToLiteral;
//# sourceMappingURL=TripleUtil.js.map