UNPKG

@comake/skl-js-engine

Version:

Standard Knowledge Language Javascript Engine

402 lines 16.8 kB
"use strict"; 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