Skip to content

Commit

Permalink
Merge pull request #41 from comake/version/0.18.0
Browse files Browse the repository at this point in the history
Version/0.18.0
  • Loading branch information
adlerfaulkner authored Dec 22, 2023
2 parents 0646803 + 6ad5f79 commit 6f7212c
Show file tree
Hide file tree
Showing 20 changed files with 2,199 additions and 102 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@comake/skl-js-engine",
"version": "0.17.0",
"version": "0.18.0",
"description": "Standard Knowledge Language Javascript Engine",
"keywords": [
"skl",
Expand Down
35 changes: 20 additions & 15 deletions src/SklEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ export class SKLEngine {
return await this.queryAdapter.executeRawUpdate(query);
}

public async executeRawEntityQuery(query: string, frame?: Frame): Promise<GraphObject> {
return await this.queryAdapter.executeRawEntityQuery(query, frame);
public async executeRawConstructQuery(query: string, frame?: Frame): Promise<GraphObject> {
return await this.queryAdapter.executeRawConstructQuery(query, frame);
}

public async find(options?: FindOneOptions): Promise<Entity> {
Expand Down Expand Up @@ -845,19 +845,24 @@ export class SKLEngine {
return getValueIfDefined<string>(verbInfoJsonLd[SKL.verbId])!;
}

private async assertVerbParamsMatchParameterSchemas(verbParams: any, verb: Entity): Promise<void> {
const verbParamsAsJsonLd = {
'@context': getValueIfDefined<ContextDefinition>(verb[SKL.parametersContext]),
'@type': SKL.Parameters,
...verbParams,
};
const parametersSchema = verb[SKL.parameters] as NodeObject;
const report = await this.convertToQuadsAndValidateAgainstShape(verbParamsAsJsonLd, parametersSchema);
if (!report.conforms) {
this.throwValidationReportError(
report,
`${getValueIfDefined(verb[RDFS.label])} parameters do not conform to the schema`,
);
private async assertVerbParamsMatchParameterSchemas(verbParams: any, verb: Verb): Promise<void> {
let parametersSchemaObject = verb[SKL.parameters];
if (parametersSchemaObject?.['@id'] && Object.keys(parametersSchemaObject).length === 1) {
parametersSchemaObject = await this.findBy({ id: parametersSchemaObject['@id'] });
}
if (verbParams && parametersSchemaObject) {
const verbParamsAsJsonLd = {
'@context': getValueIfDefined<ContextDefinition>(verb[SKL.parametersContext]),
'@type': SKL.Parameters,
...verbParams,
};
const report = await this.convertToQuadsAndValidateAgainstShape(verbParamsAsJsonLd, parametersSchemaObject);
if (!report.conforms) {
this.throwValidationReportError(
report,
`${getValueIfDefined(verb[RDFS.label])} parameters do not conform to the schema`,
);
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/storage/query-adapter/QueryAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ export interface QueryAdapter {
*/
executeRawQuery<T extends RawQueryResult>(query: string): Promise<T[]>;
/**
* Performs a raw query for data matching the query.
* Performs a raw query for entities matching the query. The query must be a CONSTRUCT query.
*/
executeRawUpdate(query: string): Promise<void>;
executeRawConstructQuery(query: string, frame?: Frame): Promise<GraphObject>;
/**
* Performs a raw query for entities matching the query. The query must be a CONSTRUCT query.
* Performs a raw query for data matching the query.
*/
executeRawEntityQuery(query: string, frame?: Frame): Promise<GraphObject>;
executeRawUpdate(query: string): Promise<void>;
/**
* Finds the first entity by a given find options.
* If entity was not found in the database it returns null.
Expand Down
14 changes: 6 additions & 8 deletions src/storage/query-adapter/sparql/SparqlQueryAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import type {
import type { QueryAdapter, RawQueryResult } from '../QueryAdapter';
import { InMemorySparqlQueryExecutor } from './query-executor/InMemorySparqlQueryExecutor';
import { SparqlEndpointQueryExecutor } from './query-executor/SparqlEndpointQueryExecutor';
import type { QueryExecutor, SelectVariableQueryResult } from './query-executor/SparqlQueryExecutor';
import type { QueryExecutor } from './query-executor/SparqlQueryExecutor';
import type { SparqlQueryAdapterOptions } from './SparqlQueryAdapterOptions';
import { SparqlQueryBuilder } from './SparqlQueryBuilder';
import { SparqlUpdateBuilder } from './SparqlUpdateBuilder';
Expand All @@ -59,19 +59,17 @@ export class SparqlQueryAdapter implements QueryAdapter {
}
}

public async executeRawQuery<T extends RawQueryResult>(
query: string,
): Promise<T[]> {
public async executeRawQuery<T extends RawQueryResult>(query: string): Promise<T[]> {
const response =
await this.queryExecutor.executeSparqlSelectAndGetDataRaw<SelectVariableQueryResult<T>>(query);
await this.queryExecutor.executeSparqlSelectAndGetDataRaw(query);
if (response.length === 0) {
return [] as T[];
}
return selectQueryResultsAsJSValues<T>(response);
}

public async executeRawEntityQuery(query: string, frame?: Frame): Promise<GraphObject> {
const response = await this.queryExecutor.executeSparqlSelectAndGetDataRaw(query);
public async executeRawConstructQuery(query: string, frame?: Frame): Promise<GraphObject> {
const response = await this.queryExecutor.executeSparqlConstructAndGetDataRaw(query);
if (response.length === 0) {
return { '@graph': []};
}
Expand Down Expand Up @@ -142,7 +140,7 @@ export class SparqlQueryAdapter implements QueryAdapter {
let entityOrder: string[] | undefined;
if (queryData.orders.length > 0 && options?.limit !== 1 && entitySelectQuery) {
const entitySelectResponse =
await this.queryExecutor.executeSparqlSelectAndGetData<SelectVariableQueryResult<any>>(entitySelectQuery);
await this.queryExecutor.executeSparqlSelectAndGetData(entitySelectQuery);
const valuesByVariable = groupSelectQueryResultsByKey(entitySelectResponse);
entityOrder = getEntityVariableValuesFromVariables(valuesByVariable);
if (entityOrder.length === 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,52 @@ export class InMemorySparqlQueryExecutor implements QueryExecutor {
};
}

public async executeSparqlSelectAndGetData<T extends Quad | SelectVariableQueryResult<any> = Quad>(
query: SelectQuery | ConstructQuery,
): Promise<T[]> {
public async executeSparqlSelectAndGetData<
TQuery extends SelectQuery | ConstructQuery,
TReturn extends Quad | SelectVariableQueryResult<any> =
TQuery extends SelectQuery ? SelectVariableQueryResult<any> : Quad
>(
query: TQuery,
): Promise<TReturn[]> {
const generatedQuery = this.sparqlGenerator.stringify(query);
return this.executeSparqlSelectAndGetDataRaw(generatedQuery, query.queryType === 'CONSTRUCT');
if (query.queryType === 'CONSTRUCT') {
return (await this.executeSparqlConstructAndGetDataRaw(generatedQuery)) as TReturn[];
}
return (await this.executeSparqlSelectAndGetDataRaw(generatedQuery)) as TReturn[];
}

public async executeSparqlSelectAndGetDataRaw(
query: string,
): Promise<SelectVariableQueryResult<any>[]> {
const stream = await this.engine.queryBindings(query, this.queryContext);
return this.getDataFromStream(stream, (row, data): void => {
if (row.entries.size > 0) {
const bindingRow: SelectVariableQueryResult<any> = {};
for (const [ key, value ] of row.entries) {
bindingRow[key] = value;
}
data.push(bindingRow);
}
});
}

public async executeSparqlSelectAndGetDataRaw<T extends Quad | SelectVariableQueryResult<any> = Quad>(
public async executeSparqlConstructAndGetDataRaw(
query: string,
isConstruct = false,
): Promise<Quad[]> {
const stream = await this.engine.queryQuads(query, this.queryContext);
return this.getDataFromStream(stream, (row, data): void => {
data.push(row);
});
}

private async getDataFromStream<T extends Quad | SelectVariableQueryResult<any> = Quad>(
stream: NodeJS.EventEmitter,
dataCallback: (row: any, data: T[]) => void,
): Promise<T[]> {
const stream = await this.engine[isConstruct ? 'queryQuads' : 'queryBindings'](
query,
this.queryContext,
);
return new Promise((resolve, reject): void => {
const data: T[] = [];
stream.on('data', (row): void => {
if (row.termType === 'Quad') {
data.push(row);
} else if (row.type === 'bindings' && row.entries.size > 0) {
const bindingRow: SelectVariableQueryResult<any> = {};
for (const [ key, value ] of row.entries) {
bindingRow[key] = value;
}
data.push(bindingRow as T);
}
dataCallback(row, data);
});

stream.on('end', (): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export class SparqlEndpointQueryExecutor implements QueryExecutor {
this.sparqlGenerator = new Generator();
}

public async executeSparqlSelectAndGetData<T extends Quad | SelectVariableQueryResult<any> = Quad>(
query: SelectQuery | ConstructQuery,
): Promise<T[]> {
public async executeSparqlSelectAndGetData<
TQuery extends SelectQuery | ConstructQuery,
TReturn extends SelectVariableQueryResult<any> | Quad =
TQuery extends SelectQuery ? SelectVariableQueryResult<any> : Quad
>(
query: TQuery,
): Promise<TReturn[]> {
const generatedQuery = this.sparqlGenerator.stringify(query);
return this.executeSparqlSelectAndGetDataRaw(generatedQuery);
}
Expand All @@ -60,6 +64,10 @@ export class SparqlEndpointQueryExecutor implements QueryExecutor {
});
}

public async executeSparqlConstructAndGetDataRaw(query: string): Promise<Quad[]> {
return await this.executeSparqlSelectAndGetDataRaw(query);
}

public async executeSparqlUpdate(query: Update): Promise<void> {
const generatedQuery = this.sparqlGenerator.stringify(query);
await this.executeRawSparqlUpdate(generatedQuery);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,25 @@ export interface QueryExecutor {
/**
* Executes a SPARQL select or construct query.
*/
executeSparqlSelectAndGetData<T extends Quad | SelectVariableQueryResult<any> = Quad>(
query: SelectQuery | ConstructQuery,
): Promise<T[]>;
executeSparqlSelectAndGetData<
TQuery extends SelectQuery | ConstructQuery,
TReturn extends SelectVariableQueryResult<any> | Quad =
TQuery extends SelectQuery ? SelectVariableQueryResult<any> : Quad
>(
query: TQuery,
): Promise<TReturn[]>;
/**
* Executes a raw SPARQL select query.
*/
executeSparqlSelectAndGetDataRaw<T extends Quad | SelectVariableQueryResult<any> = Quad>(
executeSparqlSelectAndGetDataRaw(
query: string,
): Promise<T[]>;
): Promise<SelectVariableQueryResult<any>[]>;
/**
* Executes a raw SPARQL construct query.
*/
executeSparqlConstructAndGetDataRaw(
query: string,
): Promise<Quad[]>;
/**
* Executes a SPARQL update query.
*/
Expand Down
4 changes: 2 additions & 2 deletions src/util/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ export type Verb = NodeObject & {
'@type': typeof SKL.Verb;
[RDFS.label]?: ValueObject<string>;
[SKL.parametersContext]?: ValueObject<JSONObject>;
[SKL.parameters]?: NodeShape;
[SKL.returnValue]?: NodeObject;
[SKL.parameters]?: NodeShape | ReferenceNodeObject;
[SKL.returnValue]?: NodeShape | ReferenceNodeObject;
};

export interface SeriesVerbArgs extends JSONObject {
Expand Down
18 changes: 17 additions & 1 deletion src/util/Util.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { ReferenceNodeObject } from '@comake/rmlmapper-js';
import { getIdFromNodeObjectIfDefined } from '@comake/rmlmapper-js';
import * as jsonld from 'jsonld';
import type { NodeObject, ValueObject } from 'jsonld';
import { Parser, Store } from 'n3';
import type { EntityFieldSingularValue, EntityFieldValue, JSONObject } from './Types';
import type { EntityFieldSingularValue, EntityFieldValue, JSONObject, RdfList } from './Types';
import { RDF } from './Vocabularies';

export async function convertJsonLdToQuads(jsonldDoc: any): Promise<Store> {
const nquads = await jsonld.toRDF(jsonldDoc, { format: 'application/n-quads' }) as unknown as string;
Expand Down Expand Up @@ -66,3 +69,16 @@ export function isUrl(value: any): boolean {
return false;
}
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export function rdfListToArray<T extends NodeObject>(list: { '@list': T[] } | RdfList<T>): T[] {
if (!('@list' in list)) {
return [
list[RDF.first],
...getIdFromNodeObjectIfDefined(list[RDF.rest] as ReferenceNodeObject) === RDF.nil
? []
: rdfListToArray(list[RDF.rest] as RdfList<T>),
];
}
return list['@list'];
}
Loading

0 comments on commit 6f7212c

Please sign in to comment.