UNPKG

@comake/skl-js-engine

Version:

Standard Knowledge Language Javascript Engine

518 lines (468 loc) 13.2 kB
import DataFactory from '@rdfjs/data-model'; import type { Literal, NamedNode } from '@rdfjs/types'; import type { AggregateExpression, AskQuery, BgpPattern, BindPattern, ClearDropOperation, ConstructQuery, Expression, FilterPattern, GraphPattern, GraphQuads, Grouping, GroupPattern, InsertDeleteOperation, IriTerm, OperationExpression, OptionalPattern, Ordering, Pattern, PropertyPath, SelectQuery, ServicePattern, Triple, UnionPattern, Update, UpdateOperation, ValuePatternRow, ValuesPattern, Variable } from 'sparqljs'; import { EngineConstants } from '../constants'; import type { RawQueryResult } from '../storage/query-adapter/QueryAdapter'; import type { SelectVariableQueryResult } from '../storage/query-adapter/sparql/query-executor/SparqlQueryExecutor'; import { toJSValueFromDataType } from './TripleUtil'; import { RDF, RDFS } from './Vocabularies'; export const rdfTypeNamedNode = DataFactory.namedNode(RDF.type); export const rdfsSubClassOfNamedNode = DataFactory.namedNode(RDFS.subClassOf); export const subjectNode = DataFactory.variable('subject'); export const predicateNode = DataFactory.variable('predicate'); export const objectNode = DataFactory.variable('object'); export const entityVariable = DataFactory.variable('entity'); export const rdfTypeVariable = DataFactory.variable('rdfType'); export const rdfTypesVariable = DataFactory.variable('rdfTypes'); export const countVariable = DataFactory.variable('count'); export const now = DataFactory.variable('now'); export const created = DataFactory.namedNode(EngineConstants.prop.dateCreated); export const modified = DataFactory.namedNode(EngineConstants.prop.dateModified); export const firstPredicate = DataFactory.namedNode(RDF.first); export const restPredicate = DataFactory.namedNode(RDF.rest); export const nilPredicate = DataFactory.namedNode(RDF.nil); export const anyPredicatePropertyPath = { type: 'path', pathType: '!', items: [ DataFactory.namedNode('') ] } as PropertyPath; export const allTypesAndSuperTypesPath: PropertyPath = { type: 'path', pathType: '/', items: [ rdfTypeNamedNode, { type: 'path', pathType: '*', items: [ rdfsSubClassOfNamedNode ] } ] }; export const bindNow: BindPattern = { type: 'bind', variable: now, expression: { type: 'operation', operator: 'now', args: [] } }; export const dropAll: ClearDropOperation = { type: 'drop', silent: true, graph: { type: 'graph', all: true } }; export const entityGraphTriple = { subject: subjectNode, predicate: predicateNode, object: objectNode }; export function createSparqlGraphQuads(graph: NamedNode, triples: Triple[]): GraphQuads { return { type: 'graph', name: graph, triples }; } export function createSparqlClearUpdate(graph: NamedNode): ClearDropOperation { return { type: 'clear', silent: true, graph: { type: 'graph', name: graph } }; } export function createSparqlDropUpdate(graph: NamedNode): ClearDropOperation { return { type: 'drop', silent: true, graph: { type: 'graph', name: graph } }; } export function createSparqlUpdate(updates: UpdateOperation[]): Update { return { type: 'update', prefixes: {}, updates }; } export function createSparqlGraphPattern(name: Variable | NamedNode, patterns: Pattern[]): GraphPattern { return { type: 'graph', name, patterns } as GraphPattern; } export function createSparqlConstructQuery(triples: Triple[], where: Pattern[]): ConstructQuery { return { type: 'query', prefixes: {}, queryType: 'CONSTRUCT', template: triples, where }; } export function createSparqlCountSelectQuery( subject: Variable, where: Pattern[], order: Ordering[], offset?: number ): SelectQuery { return { type: 'query', queryType: 'SELECT', variables: [ { expression: { type: 'aggregate', aggregation: 'count', distinct: true, expression: subject } as AggregateExpression, variable: countVariable } ], where, order: order.length > 0 ? order : undefined, offset, prefixes: {} }; } export function creteSparqlAskQuery(where: Pattern[]): AskQuery { return { type: 'query', queryType: 'ASK', where, prefixes: {} }; } export function createSparqlSelectGroup(patterns: Pattern[]): GroupPattern { return { type: 'group', patterns }; } export function createSparqlOptional(patterns: Pattern[]): OptionalPattern { return { type: 'optional', patterns }; } export function createSparqlUnion(patterns: Pattern[]): UnionPattern { return { type: 'union', patterns }; } export function createSparqlBasicGraphPattern(triples: Triple[]): BgpPattern { return { type: 'bgp', triples }; } export function createSparqlOptionalGraphSelection(name: Variable | NamedNode, triples: Triple[]): GraphPattern { return { type: 'graph', name: name as IriTerm, patterns: [ createSparqlOptional([ createSparqlBasicGraphPattern(triples) ]) ] }; } export function createSparqlServicePattern(serviceName: string, triples: Triple[]): ServicePattern { return { type: 'service', patterns: [ createSparqlBasicGraphPattern(triples) ], name: DataFactory.namedNode(serviceName), silent: false }; } export function ensureVariable(variableLike: string | Variable): Variable { if (typeof variableLike === 'string' && variableLike.startsWith('?')) { return DataFactory.variable(variableLike.slice(1)); } return variableLike as Variable; } export function ensureGrouping(variableLike: Variable | string): Grouping { return { expression: ensureVariable(variableLike) } as Grouping; } export function createSparqlSelectQuery( variable: Variable | Variable[], where: Pattern[], order: Ordering[], group?: Variable | Variable[], limit?: number, offset?: number ): SelectQuery { let groupings: Grouping[] | undefined; if (group) { if (Array.isArray(group)) { // eslint-disable-next-line id-length groupings = group.map(g => ensureGrouping(g)); } else { groupings = [ ensureGrouping(group) ]; } } return { type: 'query', queryType: 'SELECT', variables: Array.isArray(variable) ? variable.map(ensureVariable) : [ ensureVariable(variable) ], distinct: true, where, group: groupings, order: order.length > 0 ? order : undefined, limit, offset, prefixes: {} }; } export function createSparqlFilterWithExpression(expression: Expression): FilterPattern { return { type: 'filter', expression }; } export function createFilterPatternFromFilters(filters: Expression[]): FilterPattern { if (filters.length > 2) { return createFilterPatternFromFilters([ { type: 'operation', operator: '&&', args: filters.slice(0, 2) }, ...filters.slice(2) ]); } if (filters.length > 1) { return createSparqlFilterWithExpression({ type: 'operation', operator: '&&', args: filters }); } return createSparqlFilterWithExpression(filters[0]); } export function createSparqlBindPattern(expression: Expression, variable: Variable): BindPattern { return { type: 'bind', expression, variable } as BindPattern; } export function createSparqlEqualOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '=', args: [ leftSide, rightSide ] }; } export function createSparqlLcaseOperation(expression: Expression): OperationExpression { return { type: 'operation', operator: 'lcase', args: [ expression ] }; } export function createSparqlContainsOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: 'contains', args: [ leftSide, rightSide ] }; } export function createSparqlGtOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '>', args: [ leftSide, rightSide ] }; } export function createSparqlGteOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '>=', args: [ leftSide, rightSide ] }; } export function createSparqlLtOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '<', args: [ leftSide, rightSide ] }; } export function createSparqlLteOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '<=', args: [ leftSide, rightSide ] }; } export function createSparqlNotEqualOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: '!=', args: [ leftSide, rightSide ] }; } export function createSparqlInOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: 'in', args: [ leftSide, rightSide ] }; } export function createSparqlNotInOperation(leftSide: Expression, rightSide: Expression): OperationExpression { return { type: 'operation', operator: 'notin', args: [ leftSide, rightSide ] }; } export function createSparqlNotExistsOperation(args: Expression[]): OperationExpression { return { type: 'operation', operator: 'notexists', args }; } export function createSparqlExistsOperation(args: Expression[]): OperationExpression { return { type: 'operation', operator: 'exists', args }; } export function createSparqlInversePredicate(predicates: (IriTerm | PropertyPath)[]): PropertyPath { return { type: 'path', pathType: '^', items: predicates }; } export function createSparqlOrPredicate(predicates: (IriTerm | PropertyPath)[]): PropertyPath { return { type: 'path', pathType: '|', items: predicates }; } export function createSparqlSequencePredicate(predicates: (IriTerm | PropertyPath)[]): PropertyPath { return { type: 'path', pathType: '/', items: predicates }; } export function createSparqlZeroOrMorePredicate(predicates: (IriTerm | PropertyPath)[]): PropertyPath { return { type: 'path', pathType: '*', items: predicates }; } export function createSparqlOneOrMorePredicate(predicates: (IriTerm | PropertyPath)[]): PropertyPath { return { type: 'path', pathType: '+', items: predicates }; } export function createSparqlInsertDeleteOperation( graph: NamedNode, insertionTriples: Triple[], deletionTriples: Triple[] ): InsertDeleteOperation { return { updateType: 'insertdelete', delete: [ createSparqlGraphQuads(graph, deletionTriples) ], insert: [ createSparqlGraphQuads(graph, insertionTriples) ], where: [ createSparqlBasicGraphPattern(deletionTriples) ], using: { default: [ graph ] } } as InsertDeleteOperation; } export function selectQueryResultsAsJSValues<T extends RawQueryResult>(results: SelectVariableQueryResult<T>[]): T[] { return results.map( (result): T => Object.entries(result).reduce<T>( (obj, [ key, value ]): T => ({ ...obj, [key]: value.termType === 'Literal' ? toJSValueFromDataType(value.value, value.datatype?.value) : value.value }), // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter {} as T ) ); } export function groupSelectQueryResultsByKey( results: SelectVariableQueryResult<any>[] ): Record<string, (NamedNode | Literal)[]> { return results.reduce( (obj: Record<string, (NamedNode | Literal)[]>, result): Record<string, (NamedNode | Literal)[]> => { for (const [ key, value ] of Object.entries(result)) { if (!(key in obj)) { obj[key] = [ value ]; } else { obj[key].push(value); } } return obj; }, {} ); } export function getEntityVariableValuesFromVariables(variables: Record<string, (Literal | NamedNode)[]>): string[] { if (!(entityVariable.value in variables)) { return []; } return (variables[entityVariable.value] as NamedNode[]).map((namedNode: NamedNode): string => namedNode.value); } export function getRdfTypeVariableValuesFromVariables(variables: Record<string, (Literal | NamedNode)[]>): string[] { if (!(rdfTypesVariable.value in variables)) { return []; } return (variables[rdfTypesVariable.value] as Literal[]).flatMap((literal: Literal): string[] => literal.value.split(' | ')); } export function createValuesPatternsForVariables( valuesByVariable: Record<string, (NamedNode | Literal)[]> ): ValuesPattern[] { return Object.entries(valuesByVariable).map( ([ variableName, values ]): ValuesPattern => ({ type: 'values', values: values.map( (value): ValuePatternRow => ({ [`?${variableName}`]: value }) ) }) ); }