UNPKG

@comake/skl-js-engine

Version:

Standard Knowledge Language Javascript Engine

291 lines 15.7 kB
"use strict"; /* eslint-disable capitalized-comments */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SparqlQueryAdapter = void 0; const logger_1 = require("../../../logger"); const PerformanceLogger_1 = require("../../../util/PerformanceLogger"); const SparqlUtil_1 = require("../../../util/SparqlUtil"); const TripleUtil_1 = require("../../../util/TripleUtil"); const InMemorySparqlQueryExecutor_1 = require("./query-executor/InMemorySparqlQueryExecutor"); const SparqlEndpointQueryExecutor_1 = require("./query-executor/SparqlEndpointQueryExecutor"); const SparqlQueryBuilder_1 = require("./SparqlQueryBuilder"); const SparqlUpdateBuilder_1 = require("./SparqlUpdateBuilder"); /** * A {@link QueryAdapter} that stores data in a database through a sparql endpoint. */ class SparqlQueryAdapter { constructor(options) { this.setTimestamps = options.setTimestamps ?? false; switch (options.type) { case 'memory': this.queryExecutor = new InMemorySparqlQueryExecutor_1.InMemorySparqlQueryExecutor(); break; case 'sparql': this.queryExecutor = new SparqlEndpointQueryExecutor_1.SparqlEndpointQueryExecutor(options); break; default: throw new Error('No schema source found in setSchema args.'); } this.logger = logger_1.Logger.getInstance(); } async executeRawQuery(query) { const response = await this.queryExecutor.executeSparqlSelectAndGetDataRaw(query); if (response.length === 0) { return []; } return (0, SparqlUtil_1.selectQueryResultsAsJSValues)(response); } async executeRawConstructQuery(query, frame) { const response = await this.queryExecutor.executeSparqlConstructAndGetDataRaw(query); if (response.length === 0) { return { '@graph': [] }; } return await (0, TripleUtil_1.triplesToJsonldWithFrame)(response, frame); } async executeRawUpdate(query) { await this.queryExecutor.executeRawSparqlUpdate(query); } async find(options) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.find', async () => { const jsonld = await this.findAllAsJsonLd({ ...options, limit: 1 }); if (Array.isArray(jsonld) && !options?.skipFraming) { if (jsonld.length === 0) { return null; } if (jsonld.length === 1) { return jsonld[0]; } } return jsonld; }, { options }); } async findBy(where) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.findBy', async () => this.find({ where }), { where }); } async findAll(options) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.findAll', async () => { const jsonld = await this.findAllAsJsonLd(options); if (Array.isArray(jsonld)) { return jsonld; } return [jsonld]; }, { options }); } async findAllAsJsonLd(options) { const queryBuilder = new SparqlQueryBuilder_1.SparqlQueryBuilder(); const { where, selectionTriples, entityOrder, rdfTypes } = await this.buildFindAllQueryData(queryBuilder, options); if (entityOrder && entityOrder.length === 0) { return []; } const queryData = queryBuilder.buildEntitySelectPatternsFromOptions(SparqlUtil_1.entityVariable, options); const query = queryBuilder.buildConstructFromEntitySelectQuery(where, selectionTriples, options?.select, queryData.selectVariables); return await this.executeEntitySelectQuery(query, options, entityOrder, rdfTypes); } async buildFindAllQueryData(queryBuilder, options) { const queryData = queryBuilder.buildEntitySelectPatternsFromOptions(SparqlUtil_1.entityVariable, options); const selectQueryData = queryBuilder.buildEntitySelectPatternsFromOptions(SparqlUtil_1.entityVariable, { ...options, relations: undefined }); let rdfTypes; const wherePatterns = [...selectQueryData.where, ...selectQueryData.graphWhere]; wherePatterns.push({ type: 'bgp', triples: [ { subject: SparqlUtil_1.entityVariable, predicate: SparqlUtil_1.rdfTypeNamedNode, object: SparqlUtil_1.rdfTypeVariable } ] }); const entitySelectQuery = selectQueryData.where.length > 0 ? (0, SparqlUtil_1.createSparqlSelectQuery)([ options?.entitySelectVariable ?? SparqlUtil_1.entityVariable, SparqlUtil_1.rdfTypeVariable, ...selectQueryData.selectVariables?.map(({ variable, expression }) => { if (!expression) return variable; return { variable, expression }; }) ?? [] ], wherePatterns, selectQueryData.orders, selectQueryData.group ?? options?.group, options?.limit, options?.offset) : undefined; let entityOrder; /* If relations are present add them to where */ if ((queryData?.relationsQueryData?.unionPatterns ?? []).length > 0) { queryData?.relationsQueryData?.unionPatterns.push((0, SparqlUtil_1.createSparqlGraphPattern)(SparqlUtil_1.entityVariable, [(0, SparqlUtil_1.createSparqlBasicGraphPattern)([SparqlUtil_1.entityGraphTriple])])); } if (queryData.orders.length > 0 && options?.limit !== 1 && entitySelectQuery) { const entitySelectResponse = await this.queryExecutor.executeSparqlSelectAndGetData(entitySelectQuery); const valuesByVariable = (0, SparqlUtil_1.groupSelectQueryResultsByKey)(entitySelectResponse); entityOrder = (0, SparqlUtil_1.getEntityVariableValuesFromVariables)(valuesByVariable); if (entityOrder.length === 0) { return { where: queryData.where, selectionTriples: queryData.graphSelectionTriples, entityOrder: [] }; } const variableValueFilters = (0, SparqlUtil_1.createValuesPatternsForVariables)({ [SparqlUtil_1.entityVariable.value]: valuesByVariable[SparqlUtil_1.entityVariable.value] }); queryData.graphWhere = [...variableValueFilters, ...queryData.graphWhere]; } else if (entitySelectQuery) { // We need entity IDs for framing when: // 1. There are relations (to distinguish root entities from related entities) // 2. There's a type constraint (to handle subclass matching where SPARQL finds subclasses but JSON-LD needs exact types) const hasRelations = (queryData?.relationsQueryData?.unionPatterns ?? []).length > 0; const hasTypeConstraint = options?.where?.type !== undefined; if ((hasRelations || hasTypeConstraint) && queryData.orders.length > 0) { const entitySelectResponse = await this.queryExecutor.executeSparqlSelectAndGetData(entitySelectQuery); const valuesByVariable = (0, SparqlUtil_1.groupSelectQueryResultsByKey)(entitySelectResponse); entityOrder = queryData.orders.length > 0 ? (0, SparqlUtil_1.getEntityVariableValuesFromVariables)(valuesByVariable) : []; if (entityOrder.length === 0) { return { where: queryData.where, selectionTriples: queryData.graphSelectionTriples, entityOrder: [] }; } } else if (hasRelations || hasTypeConstraint) { const entitySelectResponse = await this.queryExecutor.executeSparqlSelectAndGetData(entitySelectQuery); const groupedResults = (0, SparqlUtil_1.groupSelectQueryResultsByKey)(entitySelectResponse); const valuesByVariable = (0, SparqlUtil_1.getRdfTypeVariableValuesFromVariables)(groupedResults); rdfTypes = [...new Set(valuesByVariable)]; // Also get entity IDs for framing to distinguish root entities from related ones // entityOrder = getEntityVariableValuesFromVariables(groupedResults); // if (entityOrder.length === 0) { // return { // where: queryData.where, // selectionTriples: queryData.graphSelectionTriples, // entityOrder: [], // rdfTypes // }; // } } // Always add the select group query to the CONSTRUCT const entitySelectGroupQuery = (0, SparqlUtil_1.createSparqlSelectGroup)([entitySelectQuery]); queryData.graphWhere.unshift(entitySelectGroupQuery); // queryData.graphWhere = [ ...queryData.where, ...queryData.graphWhere ]; } return { where: queryData.graphWhere, selectionTriples: queryData.graphSelectionTriples, entityOrder, rdfTypes }; } async executeEntitySelectQuery(query, options, entityOrder, rdfTypes) { const responseTriples = await this.queryExecutor.executeSparqlSelectAndGetData(query); return await (0, TripleUtil_1.triplesToJsonld)(responseTriples, options?.skipFraming, options?.relations, options?.where, entityOrder, rdfTypes); } async findAllBy(where) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.findAllBy', async () => this.findAll({ where }), { where }); } async exists(options) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.exists', async () => { const queryBuilder = new SparqlQueryBuilder_1.SparqlQueryBuilder(); const queryData = queryBuilder.buildEntitySelectPatternsFromOptions(SparqlUtil_1.entityVariable, options); const values = queryData.graphWhere.filter((pattern) => pattern.type === 'values'); const query = (0, SparqlUtil_1.creteSparqlAskQuery)([...values, ...queryData.where]); return await this.queryExecutor.executeAskQueryAndGetResponse(query); }, { options }); } async count(options) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.count', async () => { const queryBuilder = new SparqlQueryBuilder_1.SparqlQueryBuilder(); const queryData = queryBuilder.buildEntitySelectPatternsFromOptions(SparqlUtil_1.entityVariable, options); const values = queryData.graphWhere.filter((pattern) => pattern.type === 'values'); const query = (0, SparqlUtil_1.createSparqlCountSelectQuery)(SparqlUtil_1.entityVariable, [...values, ...queryData.where], queryData.orders, options?.offset); return await this.queryExecutor.executeSelectCountAndGetResponse(query); }, { options }); } async save(entityOrEntities) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.save', async () => { const queryBuilder = new SparqlUpdateBuilder_1.SparqlUpdateBuilder({ setTimestamps: this.setTimestamps }); const query = queryBuilder.buildUpdate(entityOrEntities); await this.queryExecutor.executeSparqlUpdate(query); return entityOrEntities; }, { entityCount: Array.isArray(entityOrEntities) ? entityOrEntities.length : 1 }); } async groupBy(options) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.groupBy', async () => { const queryBuilder = new SparqlQueryBuilder_1.SparqlQueryBuilder(); const { query: selectQuery, variableMapping } = await queryBuilder.buildGroupByQuery(options); const results = await this.queryExecutor.executeSparqlSelectAndGetData(selectQuery); // Create reverse mapping from path to variable name const reverseMapping = Object.entries(variableMapping).reduce((acc, [varName, path]) => { acc[path] = varName; return acc; }, {}); // Transform results const groupResults = results.map(result => { const group = {}; options.groupBy?.forEach(path => { const varName = reverseMapping[path]; if (!varName) { throw new Error(`No variable mapping found for path: ${path}`); } const { value } = result[varName]; // Try to convert to number if possible group[path] = Number.isNaN(Number(value)) ? value : Number(value); }); if (options.dateGrouping) { const dateGroupVarName = reverseMapping.dateGroup; group.dateGroup = result[dateGroupVarName].value; } const countVarName = reverseMapping.count; const entityIdsVarName = reverseMapping.entityIds; return { group, count: Number.parseInt(result[countVarName].value, 10), entityIds: result[entityIdsVarName].value.split(' ') }; }); return { results: groupResults, meta: { totalCount: groupResults.reduce((sum, curr) => sum + curr.count, 0), dateRange: options.dateRange, groupings: options.groupBy || [] } }; }, { options }); } async update(idOrIds, attributes) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.update', async () => { const queryBuilder = new SparqlUpdateBuilder_1.SparqlUpdateBuilder({ setTimestamps: this.setTimestamps }); const query = queryBuilder.buildPartialUpdate(idOrIds, attributes); await this.queryExecutor.executeSparqlUpdate(query); }, { idCount: Array.isArray(idOrIds) ? idOrIds.length : 1 }); } async delete(idOrIds) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.delete', async () => { const queryBuilder = new SparqlUpdateBuilder_1.SparqlUpdateBuilder(); const query = queryBuilder.buildDeleteById(idOrIds); await this.queryExecutor.executeSparqlUpdate(query); }, { idCount: Array.isArray(idOrIds) ? idOrIds.length : 1 }); } async destroy(entityOrEntities) { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.destroy', async () => { const queryBuilder = new SparqlUpdateBuilder_1.SparqlUpdateBuilder(); const query = queryBuilder.buildDelete(entityOrEntities); await this.queryExecutor.executeSparqlUpdate(query); return entityOrEntities; }, { entityCount: Array.isArray(entityOrEntities) ? entityOrEntities.length : 1 }); } async destroyAll() { return PerformanceLogger_1.PerformanceLogger.withSpan('Adapter.destroyAll', async () => { const queryBuilder = new SparqlUpdateBuilder_1.SparqlUpdateBuilder(); const query = queryBuilder.buildDeleteAll(); await this.queryExecutor.executeSparqlUpdate(query); }); } } exports.SparqlQueryAdapter = SparqlQueryAdapter; //# sourceMappingURL=SparqlQueryAdapter.js.map