From 8d364bdee5ba5ddcd6af9510a7e8ee2dc14a20a5 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Tue, 19 Oct 2021 16:26:31 +0200 Subject: [PATCH 01/35] first draft of the Queryable spec --- queryable-spec.ts | 141 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 queryable-spec.ts diff --git a/queryable-spec.ts b/queryable-spec.ts new file mode 100644 index 0000000..5725752 --- /dev/null +++ b/queryable-spec.ts @@ -0,0 +1,141 @@ + +import RDF from '@rdfjs/types'; +import {Algebra} from 'sparqlalgebrajs'; +import {EventEmitter} from 'events'; // TODO: refer to underlying interface, not to the class + +/* + * Helper union type + */ +type termName = 'subject' | 'predicate' | 'object' | 'graph'; + +/* + * Custom typings for the RDF/JS Stream interface as the current + * typings restrict the generic param Q to extensions of "BaseQuad", + * meaning it cannot be used for Bindings. + */ +export interface Stream extends EventEmitter { + read(): Q | null; +} + +/* + * Map-like representation of Bindings as using plain objects could lead + * to collisions between variable names and object properties. + * Long-term goal: maintain compatibility with the native Map class. + */ +interface Bindings { + type: 'bindings'; + get(variable: RDF.Variable): RDF.Term; + keys(): RDF.Variable[]; + entries(): [RDF.Variable, RDF.Term][]; + size: number; +} + +/* + * Bindings objects are created using a dedicated factory, keeping in line + * with DataFactory.quad(). This also helps with facilitating support for + * immutability. Basic helper methods must also be provided for the most + * common manipulations of bindings objects. + */ +interface BindingsFactory { + bindings(entries: [RDF.Variable, RDF.Term][]): Bindings; + merge(bindings: Bindings[]): Bindings; +} + +/* + * Base interfaces to represent query results. These interfaces are generic + * with respect to the types of query metadata objects. These can be extended + * by implementors. + */ + +interface BaseQueryResultMetadata { + cardinality?: number; // Cardinality estimate + order?: OrderItemsType[]; +} + +interface BaseQueryResult> { + type: 'bindings' | 'quads' | 'boolean'; + metadata(opts: Partial>): Promise; +} + +interface QueryResultBindings> extends BaseQueryResult { + type: 'bindings'; + bindings(): Promise; + stream(): Stream; + variables: RDF.Variable[]; +} + +interface QueryResultQuads> extends BaseQueryResult { + type: 'quads'; + quads(): Promise; + stream(): Stream; +} + +interface QueryResultBoolean> extends BaseQueryResult { + type: 'boolean'; + value: Promise; +} + +type QueryResult = QueryResultBindings | QueryResultQuads | QueryResultBoolean; + +/* + * Context objects provide a way to pass additional bits information to + * implementors, such as but not limited to: + * - data sources + * - base IRI for IRI resolution + * - timestamp for expression evaluation + * - query language + * - ... + */ + +// SourceType can be anything the query engine defines +// TODO: we may consider defining some standards, like 'string', RDF.Source, ... +interface QueryContext { + sources: [SourceType, ...SourceType[]]; + queryTimestamp?: Date; // Required for certain SPARQL operations such as NOW(). +} + +interface QueryStringContext extends QueryContext { + queryFormat?: QueryFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } + baseIRI?: string; // Required for parsing SPARQL queries +}; + +interface QueryAlgebraContext extends QueryContext {}; + +interface QueryFormat { + language: string; // Like 'SPARQL' + version: string; // Like '1.1' + extensions: string[]; // TODO: leave the syntax of these extensions open for now? +} + +type Algebra = {}; // TODO: define this (possible starting point: https://github.com/joachimvh/SPARQLAlgebra.js) + +/* + * Generic query interfaces. These allow engines to return any type of result + * object for any type of query, supporting the kind of flexibility required + * by engines such as Comunica. + */ + +interface Queryable { + query>(query: string, context?: ContextType): Promise>; +} + +interface QueryableAlgebra { + query>(query: Algebra, context?: ContextType): Promise>; +} + +/* + * SPARQL-constrainted query interfaces. These interfaces guarantee that result + * objects are of the expected type as defined by the SPARQL spec. + */ + +interface QueryableSparql { + ask?>(query: string, context?: ContextType): Promise>; + select?>(query: string, context?: ContextType): Promise>; + construct?>(query: string, context?: ContextType): Promise>; +} + +interface QueryableAlgebraSparql { + ask?>(query: Algebra.Ask, context?: ContextType): Promise>; + select?>(query: Algebra.Project, context?: ContextType): Promise>; + construct?>(query: Algebra.Construct, context?: ContextType): Promise>; +} From 04daba85f4c20a66c89a6e411a8bc32ed913e159 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 25 Oct 2021 11:16:26 +0200 Subject: [PATCH 02/35] adds undefined as a possible return type of BindingsFactory.merge() --- queryable-spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 5725752..9b7a385 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -38,7 +38,9 @@ interface Bindings { */ interface BindingsFactory { bindings(entries: [RDF.Variable, RDF.Term][]): Bindings; - merge(bindings: Bindings[]): Bindings; + // NOTE: returns undefined in case of conflicting bindings, i.e. bindings + // having the same variables. + merge(bindings: Bindings[]): Bindings|undefined; } /* From d017b32484df20fab8ce3dabc5fa149b897e62ab Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 25 Oct 2021 11:28:14 +0200 Subject: [PATCH 03/35] drops strong metadata typization --- queryable-spec.ts | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 9b7a385..ab94e5b 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -6,7 +6,7 @@ import {EventEmitter} from 'events'; // TODO: refer to underlying interface, not /* * Helper union type */ -type termName = 'subject' | 'predicate' | 'object' | 'graph'; +type TermName = 'subject' | 'predicate' | 'object' | 'graph'; /* * Custom typings for the RDF/JS Stream interface as the current @@ -49,37 +49,34 @@ interface BindingsFactory { * by implementors. */ -interface BaseQueryResultMetadata { - cardinality?: number; // Cardinality estimate +interface QueryResultMetadata { + cardinality?: number; order?: OrderItemsType[]; + [key: string]: any; } -interface BaseQueryResult> { +interface BaseQueryResult { type: 'bindings' | 'quads' | 'boolean'; - metadata(opts: Partial>): Promise; + metadata(opts: { [key: string]: any }): Promise>; } -interface QueryResultBindings> extends BaseQueryResult { +interface QueryResultBindings extends BaseQueryResult { type: 'bindings'; - bindings(): Promise; stream(): Stream; variables: RDF.Variable[]; } -interface QueryResultQuads> extends BaseQueryResult { +interface QueryResultQuads extends BaseQueryResult { type: 'quads'; - quads(): Promise; stream(): Stream; } -interface QueryResultBoolean> extends BaseQueryResult { +interface QueryResultBoolean extends BaseQueryResult { type: 'boolean'; - value: Promise; + value(): Promise; } -type QueryResult = QueryResultBindings | QueryResultQuads | QueryResultBoolean; - -/* +type QueryResult = QueryResultBindings | QueryResultQuads | QueryResultBoolean;/* * Context objects provide a way to pass additional bits information to * implementors, such as but not limited to: * - data sources @@ -118,11 +115,11 @@ type Algebra = {}; // TODO: define this (possible starting point: https://github */ interface Queryable { - query>(query: string, context?: ContextType): Promise>; + query>(query: string, context?: ContextType): Promise; } interface QueryableAlgebra { - query>(query: Algebra, context?: ContextType): Promise>; + query>(query: Algebra, context?: ContextType): Promise; } /* @@ -131,13 +128,13 @@ interface QueryableAlgebra { */ interface QueryableSparql { - ask?>(query: string, context?: ContextType): Promise>; - select?>(query: string, context?: ContextType): Promise>; - construct?>(query: string, context?: ContextType): Promise>; + boolean?>(query: string, context?: ContextType): Promise; + bindings?>(query: string, context?: ContextType): Promise; + quads?>(query: string, context?: ContextType): Promise; } interface QueryableAlgebraSparql { - ask?>(query: Algebra.Ask, context?: ContextType): Promise>; - select?>(query: Algebra.Project, context?: ContextType): Promise>; - construct?>(query: Algebra.Construct, context?: ContextType): Promise>; + boolean?>(query: Algebra.Ask, context?: ContextType): Promise; + bindings?>(query: Algebra.Project, context?: ContextType): Promise; + quads?>(query: Algebra.Construct, context?: ContextType): Promise; } From ea71d639428b2510808bbbad7f6f0879218300ac Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 3 Nov 2021 20:22:30 +0100 Subject: [PATCH 04/35] adds all the interfaces and types from the WebIDL version of the Filterable spec --- queryable-spec.ts | 249 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 225 insertions(+), 24 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index ab94e5b..617f627 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -1,7 +1,11 @@ import RDF from '@rdfjs/types'; -import {Algebra} from 'sparqlalgebrajs'; -import {EventEmitter} from 'events'; // TODO: refer to underlying interface, not to the class +import { Algebra } from 'sparqlalgebrajs'; +import { EventEmitter } from 'events'; // TODO: refer to underlying interface, not to the class + +/****************************************************************************** + COMMON INTERFACES AND TYPES + *****************************************************************************/ /* * Helper union type @@ -17,9 +21,208 @@ export interface Stream extends EventEmitter { read(): Q | null; } + +/****************************************************************************** + FILTERABLE SOURCE + *****************************************************************************/ + + +/** + * QueryResultMetadataOptions is an abstract interface that represents a generic + * expression over a stream of quads. + */ +interface Expression { + /** + * Value that identifies the concrete interface of the expression, since the + * Expression itself is not directly instantiated. Possible values include + * "operator" and "term". + */ + expressionType: string; +}; + +/** + * An OperatorExpression is represents an expression that applies a given + * operator on given sub-expressions. + * + * The WebIDL definition of the Filterable spec contains a list of supported + * operators: https://rdf.js.org/query-spec/#expression-operators + */ +interface OperatorExpression extends Expression { + + /** + * Contains the constant "operator". + */ + expressionType: 'operator'; + + /** + * Value that identifies an operator. Possible values can be found in the + * list of operators. + */ + operator: string; + + /** + * Array of Expression's on to which the given operator applies. The length + * of this array depends on the operator. + */ + args: Expression[]; +}; + +/** + * A TermExpression is an expression that contains a Term. + */ +interface TermExpression { + + /** + * The constant "term". + */ + expressionType: 'term'; + + /** + * a Term. + */ + term: RDF.Term; +} + +/** + * ExpressionFactory enables expressions to be created in an idiomatic manner. + */ +interface ExpressionFactory { + + /** + * Creates a new OperatorExpression instance for the given operator and array of arguments. + */ + operatorExpression(operator: string, args: Expression[]): OperatorExpression; + + /** + * Creates a new TermExpression instance for the given term. + */ + termExpression(term: RDF.Term): TermExpression; +}; + + +/** + * QueryResultMetadataCount is part of the QueryResultMetadata interface to + * represent metadata about the number of quads in the result stream. + */ +interface FilterableResultMetadataCount { + /** + * indicates the type of counting that was done, and MUST either be + * "estimate" or "exact". + */ + type: 'estimate' | 'exact'; + + /** + * Indicates an estimate of the number of quads in the stream if + * type = "estimate", or the exact number of quads in the stream if + * type = "exact". + */ + value: number; +}; + +/** + * A QueryResultMetadata is an object that contains metadata about a certain + * query result, such as invoking FilterableSource.matchExpression. + */ +interface FilterableResultMetadata { + + /** + * count is an optional field that contains metadata about the number of + * quads in the result stream. + */ + count?: FilterableResultMetadataCount; +}; + +/** + * A QueryResultMetadataOptions is an object that gives suggestions on what + * type of metadata is desired, such as when invoking FilterResult.metadata. + */ +interface FilterableResultMetadataOptions { + + /** + * optional field that MAY either contain "estimate" or "exact". If defined, + * this type MUST correspond to the type in QueryResultMetadataCount. + */ + count?: 'estimate' | 'exact'; +}; + +/** + * A FilterResult is an object that represents the result of a filter + * expression of FilterableSource for a given quad pattern and expression. + * It MAY create results lazily after one of its methods is invoked. + */ +interface FilterableResult { + + /** + * Returns a Stream containing all the quads that matched the given quad + * pattern and expression. + */ + quads(): Stream; + + /** + * Asynchronously returns a QueryResultMetadata, that contains the metadata + * of the current result. + */ + metadata(opts?: FilterableResultMetadataOptions): Promise; + + /** + * Asynchronously returns a boolean indicating if the requested expression is + * supported by the FilterableSource. If it returns true, quads() and + * metadata() MAY produce a valid result. If it returns false, quads() MUST + * return a stream emitting an error, and metadata() MUST reject. + */ + isSupported(): Promise; +}; +/* + * A FilterableSource is an object that produces a FilterableSourceResult that + * can emit quads. The emitted quads can be directly contained in this + * FilterableSourceo bject, or they can be generated on the fly. + * + * FilterableSource is not necessarily an extension of the RDF/JS Source + * interface, but implementers MAY decide to implement both at the same time. + * + * matchExpression() Returns a FilterableSourceResult that contains a quad + * stream that processes all quads matching the quad pattern and the expression. + * + * When a Term parameter is defined, and is a NamedNode, Literal or BlankNode, + * it must match each produced quad, according to the Quad.equals semantics. + * When a Term parameter is a Variable, or it is undefined, it acts as a + * wildcard, and can match with any Term. + * + * NOTES: + * - When matching with graph set to undefined or null it MUST match all the + * graphs (sometimes called the union graph). To match only the default graph + * set graph to a DefaultGraph. + * - When an Expression parameter is defined, the complete quad stream is + * filtered according to this expression. When it is undefined, no filter is + * applied. + * + * If parameters of type Variable with an equal variable name are in place, + * then the corresponding quad components in the resulting quad stream MUST be + * equal. + * Expression's MAY contain Variable Term's. If their variable names are equal + * to Variable's in the given quad pattern, then the Expression MUST be + * instantiated for each variable's binding in the resulting quad stream when + * applying the Expression filter. + */ +interface FilterableSource { + matchExpression( + subject?: RDF.Term, + predicate?: RDF.Term, + obj?: RDF.Term, + graph?: RDF.Term, + expression?: Expression, + ): FilterableResult; +}; + + +/****************************************************************************** + FILTERABLE SOURCE + *****************************************************************************/ + /* * Map-like representation of Bindings as using plain objects could lead * to collisions between variable names and object properties. + * * Long-term goal: maintain compatibility with the native Map class. */ interface Bindings { @@ -49,34 +252,34 @@ interface BindingsFactory { * by implementors. */ -interface QueryResultMetadata { +interface QueryableResultMetadata { cardinality?: number; order?: OrderItemsType[]; [key: string]: any; } -interface BaseQueryResult { +interface BaseQueryableResult { type: 'bindings' | 'quads' | 'boolean'; - metadata(opts: { [key: string]: any }): Promise>; + metadata(opts: { [key: string]: any }): Promise>; } -interface QueryResultBindings extends BaseQueryResult { +interface QueryableResultBindings extends BaseQueryableResult { type: 'bindings'; stream(): Stream; variables: RDF.Variable[]; } -interface QueryResultQuads extends BaseQueryResult { +interface QueryableResultQuads extends BaseQueryableResult { type: 'quads'; stream(): Stream; } -interface QueryResultBoolean extends BaseQueryResult { +interface QueryableResultBoolean extends BaseQueryableResult { type: 'boolean'; value(): Promise; } -type QueryResult = QueryResultBindings | QueryResultQuads | QueryResultBoolean;/* +type QueryableResult = QueryableResultBindings | QueryableResultQuads | QueryableResultBoolean;/* * Context objects provide a way to pass additional bits information to * implementors, such as but not limited to: * - data sources @@ -88,25 +291,23 @@ type QueryResult = QueryResultBindings | QueryResultQuads | QueryResultBoolean;/ // SourceType can be anything the query engine defines // TODO: we may consider defining some standards, like 'string', RDF.Source, ... -interface QueryContext { +interface QueryableContext { sources: [SourceType, ...SourceType[]]; queryTimestamp?: Date; // Required for certain SPARQL operations such as NOW(). } -interface QueryStringContext extends QueryContext { - queryFormat?: QueryFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } +interface QueryableStringContext extends QueryableContext { + queryFormat?: QueryableFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } baseIRI?: string; // Required for parsing SPARQL queries }; -interface QueryAlgebraContext extends QueryContext {}; +interface QueryableAlgebraContext extends QueryableContext {}; -interface QueryFormat { +interface QueryableFormat { language: string; // Like 'SPARQL' version: string; // Like '1.1' extensions: string[]; // TODO: leave the syntax of these extensions open for now? } - -type Algebra = {}; // TODO: define this (possible starting point: https://github.com/joachimvh/SPARQLAlgebra.js) /* * Generic query interfaces. These allow engines to return any type of result @@ -115,11 +316,11 @@ type Algebra = {}; // TODO: define this (possible starting point: https://github */ interface Queryable { - query>(query: string, context?: ContextType): Promise; + query>(query: string, context?: ContextType): Promise; } interface QueryableAlgebra { - query>(query: Algebra, context?: ContextType): Promise; + query>(query: Algebra.Operation, context?: ContextType): Promise; } /* @@ -128,13 +329,13 @@ interface QueryableAlgebra { */ interface QueryableSparql { - boolean?>(query: string, context?: ContextType): Promise; - bindings?>(query: string, context?: ContextType): Promise; - quads?>(query: string, context?: ContextType): Promise; + boolean?>(query: string, context?: ContextType): Promise; + bindings?>(query: string, context?: ContextType): Promise; + quads?>(query: string, context?: ContextType): Promise; } interface QueryableAlgebraSparql { - boolean?>(query: Algebra.Ask, context?: ContextType): Promise; - bindings?>(query: Algebra.Project, context?: ContextType): Promise; - quads?>(query: Algebra.Construct, context?: ContextType): Promise; + boolean?>(query: Algebra.Ask, context?: ContextType): Promise; + bindings?>(query: Algebra.Project, context?: ContextType): Promise; + quads?>(query: Algebra.Construct, context?: ContextType): Promise; } From 14e403ed0a1cbc54cf75efa2a78c85c065ee9f92 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 3 Nov 2021 20:35:12 +0100 Subject: [PATCH 05/35] adds limit, offset and available orders based on #8 --- queryable-spec.ts | 56 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 617f627..f868e35 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -118,6 +118,41 @@ interface FilterableResultMetadataCount { */ value: number; }; + +/** + * TBD + */ + interface FilterableCost { + /** + * An estimation of how many iterations over items are executed. + * This is used to determine the CPU cost. + */ + iterations: number; + /** + * An estimation of how many items are stored in memory. + * This is used to determine the memory cost. + */ + persistedItems: number; + /** + * An estimation of how many items block the stream. + * This is used to determine the time the stream is not progressing anymore. + */ + blockingItems: number; + /** + * An estimation of the time to request items from sources. + * This estimation can be based on the `cardinality`, `pageSize`, and `requestTime` metadata entries. + * This is used to determine the I/O cost. + */ + requestTime: number; +} + +/** + * TBD + */ +interface FilterableOrder { + cost: FilterableCost; + terms: { term: TermName, direction: 'asc' | 'desc' }[]; +} /** * A QueryResultMetadata is an object that contains metadata about a certain @@ -126,10 +161,16 @@ interface FilterableResultMetadataCount { interface FilterableResultMetadata { /** - * count is an optional field that contains metadata about the number of - * quads in the result stream. + * An optional field that contains metadata about the number of quads in the + * result stream. */ count?: FilterableResultMetadataCount; + + /** + * An optional field that contains the available options for quad sorting + * based on the provided pattern, expression and options. + */ + availableOrders?: FilterableOrder[]; }; /** @@ -145,6 +186,8 @@ interface FilterableResultMetadataOptions { count?: 'estimate' | 'exact'; }; + + /** * A FilterResult is an object that represents the result of a filter * expression of FilterableSource for a given quad pattern and expression. @@ -156,7 +199,7 @@ interface FilterableResult { * Returns a Stream containing all the quads that matched the given quad * pattern and expression. */ - quads(): Stream; + quads(opts?: { order?: FilterableOrder }): Stream; /** * Asynchronously returns a QueryResultMetadata, that contains the metadata @@ -172,6 +215,7 @@ interface FilterableResult { */ isSupported(): Promise; }; + /* * A FilterableSource is an object that produces a FilterableSourceResult that * can emit quads. The emitted quads can be directly contained in this @@ -211,12 +255,16 @@ interface FilterableSource { obj?: RDF.Term, graph?: RDF.Term, expression?: Expression, + opts?: { + limit?: number; + offset?: number; + }, ): FilterableResult; }; /****************************************************************************** - FILTERABLE SOURCE + QUERYABLE SOURCE *****************************************************************************/ /* From 3400d5e13cff495a237f4f86a66244a648af88ce Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 3 Nov 2021 20:39:34 +0100 Subject: [PATCH 06/35] drops use of generics in Queryable(Algebra)Context as per #7 --- queryable-spec.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index f868e35..7250874 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -342,6 +342,7 @@ type QueryableResult = QueryableResultBindings | QueryableResultQuads | Queryabl interface QueryableContext { sources: [SourceType, ...SourceType[]]; queryTimestamp?: Date; // Required for certain SPARQL operations such as NOW(). + [key: string]: any; } interface QueryableStringContext extends QueryableContext { @@ -364,11 +365,11 @@ interface QueryableFormat { */ interface Queryable { - query>(query: string, context?: ContextType): Promise; + query(query: string, context?: QueryableStringContext): Promise; } interface QueryableAlgebra { - query>(query: Algebra.Operation, context?: ContextType): Promise; + query(query: Algebra.Operation, context?: QueryableAlgebraContext): Promise; } /* @@ -377,13 +378,13 @@ interface QueryableAlgebra { */ interface QueryableSparql { - boolean?>(query: string, context?: ContextType): Promise; - bindings?>(query: string, context?: ContextType): Promise; - quads?>(query: string, context?: ContextType): Promise; + boolean?(query: string, context?: QueryableContext): Promise; + bindings?(query: string, context?: QueryableContext): Promise; + quads?(query: string, context?: QueryableContext): Promise; } interface QueryableAlgebraSparql { - boolean?>(query: Algebra.Ask, context?: ContextType): Promise; - bindings?>(query: Algebra.Project, context?: ContextType): Promise; - quads?>(query: Algebra.Construct, context?: ContextType): Promise; + boolean?(query: Algebra.Ask, context?: QueryableAlgebraContext): Promise; + bindings?(query: Algebra.Project, context?: QueryableAlgebraContext): Promise; + quads?(query: Algebra.Construct, context?: QueryableAlgebraContext): Promise; } From e84c9a978195ba83baf5ebc63d079715e6e67f7f Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 5 Nov 2021 17:35:16 +0100 Subject: [PATCH 07/35] uses generics to allow implementors to specify supported return types, unifies cost and order types across Filterable and Queryable, drops dependency on sparqljsalgebra --- queryable-spec.ts | 163 +++++++++++++++++++++++++++------------------- 1 file changed, 97 insertions(+), 66 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 7250874..df3d561 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -1,6 +1,5 @@ import RDF from '@rdfjs/types'; -import { Algebra } from 'sparqlalgebrajs'; import { EventEmitter } from 'events'; // TODO: refer to underlying interface, not to the class /****************************************************************************** @@ -21,6 +20,47 @@ export interface Stream extends EventEmitter { read(): Q | null; } +/** + * QueryOperationCost represents the cost of a given query operation. + */ + interface QueryOperationCost { + /** + * An estimation of how many iterations over items are executed. + * This is used to determine the CPU cost. + */ + iterations: number; + /** + * An estimation of how many items are stored in memory. + * This is used to determine the memory cost. + */ + persistedItems: number; + /** + * An estimation of how many items block the stream. + * This is used to determine the time the stream is not progressing anymore. + */ + blockingItems: number; + /** + * An estimation of the time to request items from sources. + * This estimation can be based on the `cardinality`, `pageSize`, and `requestTime` metadata entries. + * This is used to determine the I/O cost. + */ + requestTime: number; +} + +/** + * QueryOperationOrder represents an ordering of the results of a given + * query operation. QueryOperationOrder objects can be returned by + * implementations of both the Filterable (quad orderings) and Queryable + * (bindings orderings) interfaces. Furthermore, QueryOperationObjects are + * used to represent both available orderings (i.e. orderings that may be + * requested by callers) and returned orderings (i.e. orderings followed + * by the returned iterators). + */ +interface QueryOperationOrder { + cost: QueryOperationCost; + terms: { term: T, direction: 'asc' | 'desc' }[]; +} + /****************************************************************************** FILTERABLE SOURCE @@ -118,41 +158,6 @@ interface FilterableResultMetadataCount { */ value: number; }; - -/** - * TBD - */ - interface FilterableCost { - /** - * An estimation of how many iterations over items are executed. - * This is used to determine the CPU cost. - */ - iterations: number; - /** - * An estimation of how many items are stored in memory. - * This is used to determine the memory cost. - */ - persistedItems: number; - /** - * An estimation of how many items block the stream. - * This is used to determine the time the stream is not progressing anymore. - */ - blockingItems: number; - /** - * An estimation of the time to request items from sources. - * This estimation can be based on the `cardinality`, `pageSize`, and `requestTime` metadata entries. - * This is used to determine the I/O cost. - */ - requestTime: number; -} - -/** - * TBD - */ -interface FilterableOrder { - cost: FilterableCost; - terms: { term: TermName, direction: 'asc' | 'desc' }[]; -} /** * A QueryResultMetadata is an object that contains metadata about a certain @@ -170,7 +175,7 @@ interface FilterableResultMetadata { * An optional field that contains the available options for quad sorting * based on the provided pattern, expression and options. */ - availableOrders?: FilterableOrder[]; + availableOrders?: QueryOperationOrder[]; }; /** @@ -199,7 +204,7 @@ interface FilterableResult { * Returns a Stream containing all the quads that matched the given quad * pattern and expression. */ - quads(opts?: { order?: FilterableOrder }): Stream; + quads(opts?: { order?: QueryOperationOrder }): Stream; /** * Asynchronously returns a QueryResultMetadata, that contains the metadata @@ -269,15 +274,21 @@ interface FilterableSource { /* * Map-like representation of Bindings as using plain objects could lead - * to collisions between variable names and object properties. - * - * Long-term goal: maintain compatibility with the native Map class. + * to collisions between variable names and object properties. Support for + * immutability is required (but implementations are free to be mutable) which + * determines the return value of the set() and delete() methods to be an + * instance of Bindings (potentially a different one). */ interface Bindings { type: 'bindings'; - get(variable: RDF.Variable): RDF.Term; - keys(): RDF.Variable[]; - entries(): [RDF.Variable, RDF.Term][]; + has: (key: RDF.Variable) => boolean; + get: (key: RDF.Variable) => RDF.Term | undefined; + set: (key: RDF.Variable, value: RDF.Term) => Bindings; + delete: (key: RDF.Variable) => Bindings; + keys: () => Iterator; + values: () => Iterator; + entries: () => Iterator<[RDF.Variable, RDF.Term]>; + forEach: (fn: (value: RDF.Term, key: RDF.Variable) => any) => void; size: number; } @@ -288,10 +299,15 @@ interface Bindings { * common manipulations of bindings objects. */ interface BindingsFactory { - bindings(entries: [RDF.Variable, RDF.Term][]): Bindings; - // NOTE: returns undefined in case of conflicting bindings, i.e. bindings - // having the same variables. - merge(bindings: Bindings[]): Bindings|undefined; + bindings: (entries?: [RDF.Variable, RDF.Term][]) => Bindings; + filter: (bindings: Bindings, fn: (value: RDF.Term, key: RDF.Variable) => boolean) => Bindings; + map: (bindings: Bindings, fn: (value: RDF.Term, key: RDF.Variable) => RDF.Term) => Bindings; + merge: (left: Bindings, right: Bindings) => Bindings; + mergeWith: ( + merger: (left: RDF.Term, right: RDF.Term, key: RDF.Variable) => RDF.Term, + left: Bindings, + right: Bindings, + ) => Bindings; } /* @@ -300,34 +316,42 @@ interface BindingsFactory { * by implementors. */ -interface QueryableResultMetadata { +interface QueryableResultMetadata { cardinality?: number; - order?: OrderItemsType[]; + availableOrders?: QueryOperationOrder[]; [key: string]: any; } -interface BaseQueryableResult { - type: 'bindings' | 'quads' | 'boolean'; - metadata(opts: { [key: string]: any }): Promise>; +interface BaseQueryableResult { + type: 'bindings' | 'quads' | 'boolean' | 'void'; } -interface QueryableResultBindings extends BaseQueryableResult { +interface QueryableResultBindings extends BaseQueryableResult { type: 'bindings'; - stream(): Stream; + bindings(opts?: { order?: QueryOperationOrder }): Stream; variables: RDF.Variable[]; + metadata(opts: { [key: string]: any }): Promise>; } -interface QueryableResultQuads extends BaseQueryableResult { +interface QueryableResultQuads extends BaseQueryableResult { type: 'quads'; - stream(): Stream; + quads(opts?: { order?: QueryOperationOrder }): Stream; + metadata(opts: { [key: string]: any }): Promise>; } -interface QueryableResultBoolean extends BaseQueryableResult { +interface QueryableResultBoolean extends BaseQueryableResult { type: 'boolean'; value(): Promise; } -type QueryableResult = QueryableResultBindings | QueryableResultQuads | QueryableResultBoolean;/* +interface QueryableResultVoid extends BaseQueryableResult { + type: 'void'; + execute(): Promise; +} + +type QueryableResult = QueryableResultBindings | QueryableResultBoolean | QueryableResultQuads | QueryableResultVoid; + +/* * Context objects provide a way to pass additional bits information to * implementors, such as but not limited to: * - data sources @@ -358,18 +382,23 @@ interface QueryableFormat { extensions: string[]; // TODO: leave the syntax of these extensions open for now? } +/** + * Placeholder to represent SPARQL Algebra trees. + */ +type Algebra = {}; + /* * Generic query interfaces. These allow engines to return any type of result * object for any type of query, supporting the kind of flexibility required * by engines such as Comunica. */ -interface Queryable { - query(query: string, context?: QueryableStringContext): Promise; +interface Queryable { + query(query: string, context?: QueryableStringContext): Promise; } -interface QueryableAlgebra { - query(query: Algebra.Operation, context?: QueryableAlgebraContext): Promise; +interface QueryableAlgebra { + query(query: Algebra, context?: QueryableAlgebraContext): Promise; } /* @@ -381,10 +410,12 @@ interface QueryableSparql { boolean?(query: string, context?: QueryableContext): Promise; bindings?(query: string, context?: QueryableContext): Promise; quads?(query: string, context?: QueryableContext): Promise; + void?(query: string, context?: QueryableContext): Promise; } interface QueryableAlgebraSparql { - boolean?(query: Algebra.Ask, context?: QueryableAlgebraContext): Promise; - bindings?(query: Algebra.Project, context?: QueryableAlgebraContext): Promise; - quads?(query: Algebra.Construct, context?: QueryableAlgebraContext): Promise; + boolean?(query: Algebra, context?: QueryableAlgebraContext): Promise; + bindings?(query: Algebra, context?: QueryableAlgebraContext): Promise; + quads?(query: Algebra, context?: QueryableAlgebraContext): Promise; + void?(query: Algebra, context?: QueryableContext): Promise; } From 3857a7572e48fa0a21f1490af75f81c4ed3bdd8f Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 12:53:49 +0100 Subject: [PATCH 08/35] renames value methods in intermediate result objects to execute() and promisifies their return type --- queryable-spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index df3d561..80070aa 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -204,7 +204,7 @@ interface FilterableResult { * Returns a Stream containing all the quads that matched the given quad * pattern and expression. */ - quads(opts?: { order?: QueryOperationOrder }): Stream; + quads(opts?: { order?: QueryOperationOrder }): Promise>; /** * Asynchronously returns a QueryResultMetadata, that contains the metadata @@ -328,20 +328,20 @@ interface BaseQueryableResult { interface QueryableResultBindings extends BaseQueryableResult { type: 'bindings'; - bindings(opts?: { order?: QueryOperationOrder }): Stream; + execute(opts?: { order?: QueryOperationOrder }): Promise>; variables: RDF.Variable[]; metadata(opts: { [key: string]: any }): Promise>; } interface QueryableResultQuads extends BaseQueryableResult { type: 'quads'; - quads(opts?: { order?: QueryOperationOrder }): Stream; + execute(opts?: { order?: QueryOperationOrder }): Promise>; metadata(opts: { [key: string]: any }): Promise>; } interface QueryableResultBoolean extends BaseQueryableResult { type: 'boolean'; - value(): Promise; + execute(): Promise; } interface QueryableResultVoid extends BaseQueryableResult { From 8b076acb479c1b76346e2fce7ba66a6a57dbba63 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 12:57:10 +0100 Subject: [PATCH 09/35] adds support for custom properties in QueryOperationCost --- queryable-spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/queryable-spec.ts b/queryable-spec.ts index 80070aa..0d68ed5 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -45,6 +45,10 @@ export interface Stream extends EventEmitter { * This is used to determine the I/O cost. */ requestTime: number; + /** + * Custom properties + */ + [key: string]: any; } /** From 79a5f804b1ef9e2fa4ae58cd7f4ecc7e4e850d46 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 14:32:49 +0100 Subject: [PATCH 10/35] aliases Algebra to any --- queryable-spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 0d68ed5..3ab6397 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -388,8 +388,10 @@ interface QueryableFormat { /** * Placeholder to represent SPARQL Algebra trees. + * Algebra typings are TBD. Reference implementations include: + * - https://www.npmjs.com/package/sparqlalgebrajs */ -type Algebra = {}; +type Algebra = any; /* * Generic query interfaces. These allow engines to return any type of result From 331a63ce943ad210e85a16fbb7b48869614e04e5 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 14:41:37 +0100 Subject: [PATCH 11/35] makes Bindings extend Iterable --- queryable-spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 3ab6397..674f1e4 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -283,7 +283,7 @@ interface FilterableSource { * determines the return value of the set() and delete() methods to be an * instance of Bindings (potentially a different one). */ -interface Bindings { +interface Bindings extends Iterable<[RDF.Variable, RDF.Term]> { type: 'bindings'; has: (key: RDF.Variable) => boolean; get: (key: RDF.Variable) => RDF.Term | undefined; @@ -294,6 +294,7 @@ interface Bindings { entries: () => Iterator<[RDF.Variable, RDF.Term]>; forEach: (fn: (value: RDF.Term, key: RDF.Variable) => any) => void; size: number; + [Symbol.iterator]: () => Iterator<[RDF.Variable, RDF.Term]>; } /* From d24f360bef74f1f7bf0b2a680dc48df7cc430c25 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 14:45:20 +0100 Subject: [PATCH 12/35] moves mutation methods from Bindings to BindingsFactory --- queryable-spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 674f1e4..2bfe57d 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -287,8 +287,6 @@ interface Bindings extends Iterable<[RDF.Variable, RDF.Term]> { type: 'bindings'; has: (key: RDF.Variable) => boolean; get: (key: RDF.Variable) => RDF.Term | undefined; - set: (key: RDF.Variable, value: RDF.Term) => Bindings; - delete: (key: RDF.Variable) => Bindings; keys: () => Iterator; values: () => Iterator; entries: () => Iterator<[RDF.Variable, RDF.Term]>; @@ -313,6 +311,8 @@ interface BindingsFactory { left: Bindings, right: Bindings, ) => Bindings; + set: (bindings: Bindings, key: RDF.Variable, value: RDF.Term) => Bindings; + delete: (bindings: Bindings, key: RDF.Variable) => Bindings; } /* From 93ed4301ab3e3e0b4173701bd812cdcc870a0765 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 14:54:38 +0100 Subject: [PATCH 13/35] renames FilterableResult#quads() to #execute() to align with QueryableResult#execute() --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 2bfe57d..a3d3b88 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -208,7 +208,7 @@ interface FilterableResult { * Returns a Stream containing all the quads that matched the given quad * pattern and expression. */ - quads(opts?: { order?: QueryOperationOrder }): Promise>; + execute(opts?: { order?: QueryOperationOrder }): Promise>; /** * Asynchronously returns a QueryResultMetadata, that contains the metadata From c7a7e466eee8801ff69fc5a0fc7191d274af0774 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Mon, 22 Nov 2021 21:30:02 +0100 Subject: [PATCH 14/35] drops BaseQueryableResult --- queryable-spec.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index a3d3b88..aaa4ea6 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -327,29 +327,25 @@ interface QueryableResultMetadata }): Promise>; variables: RDF.Variable[]; metadata(opts: { [key: string]: any }): Promise>; } -interface QueryableResultQuads extends BaseQueryableResult { +interface QueryableResultQuads { type: 'quads'; execute(opts?: { order?: QueryOperationOrder }): Promise>; metadata(opts: { [key: string]: any }): Promise>; } -interface QueryableResultBoolean extends BaseQueryableResult { +interface QueryableResultBoolean { type: 'boolean'; execute(): Promise; } -interface QueryableResultVoid extends BaseQueryableResult { +interface QueryableResultVoid { type: 'void'; execute(): Promise; } From aeff5ba0ee5f1fa6600760a90befee0e97cba324 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 09:53:50 +0100 Subject: [PATCH 15/35] FilterableResultMetadataCount becomes FilterableResultMetadataCount Co-authored-by: Ruben Taelman --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index aaa4ea6..5d3e231 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -148,7 +148,7 @@ interface ExpressionFactory { * QueryResultMetadataCount is part of the QueryResultMetadata interface to * represent metadata about the number of quads in the result stream. */ -interface FilterableResultMetadataCount { +interface FilterableResultMetadataCardinality { /** * indicates the type of counting that was done, and MUST either be * "estimate" or "exact". From bc1088161fd8e262ea9b73278173adef887ea591 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 09:54:11 +0100 Subject: [PATCH 16/35] count becomes cardinality Co-authored-by: Ruben Taelman --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 5d3e231..1c1c80e 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -173,7 +173,7 @@ interface FilterableResultMetadata { * An optional field that contains metadata about the number of quads in the * result stream. */ - count?: FilterableResultMetadataCount; + cardinality?: FilterableResultMetadataCount; /** * An optional field that contains the available options for quad sorting From e32c527c770966bc847a079b6aca49f1ff870cc6 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 09:54:35 +0100 Subject: [PATCH 17/35] count becomes cardinality Co-authored-by: Ruben Taelman --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 1c1c80e..2575fb6 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -192,7 +192,7 @@ interface FilterableResultMetadataOptions { * optional field that MAY either contain "estimate" or "exact". If defined, * this type MUST correspond to the type in QueryResultMetadataCount. */ - count?: 'estimate' | 'exact'; + cardinality?: 'estimate' | 'exact'; }; From 910f6ec93f6c75037fee37c9c2f6846b5db2be7d Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 09:58:07 +0100 Subject: [PATCH 18/35] QueryableContext becomes QueryableStringContext Co-authored-by: Ruben Taelman --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 2575fb6..d0181db 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -410,7 +410,7 @@ interface QueryableAlgebra { */ interface QueryableSparql { - boolean?(query: string, context?: QueryableContext): Promise; + boolean?(query: string, context?: QueryableStringContext): Promise; bindings?(query: string, context?: QueryableContext): Promise; quads?(query: string, context?: QueryableContext): Promise; void?(query: string, context?: QueryableContext): Promise; From 75e2e5114625e59ceac679b5c0e1f36616e76866 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 10:04:24 +0100 Subject: [PATCH 19/35] offset becomes start, limit becomes length in FilterableSource --- queryable-spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index d0181db..708cbf2 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -173,7 +173,7 @@ interface FilterableResultMetadata { * An optional field that contains metadata about the number of quads in the * result stream. */ - cardinality?: FilterableResultMetadataCount; + cardinality?: FilterableResultMetadataCardinality; /** * An optional field that contains the available options for quad sorting @@ -265,8 +265,8 @@ interface FilterableSource { graph?: RDF.Term, expression?: Expression, opts?: { - limit?: number; - offset?: number; + length?: number; + start?: number; }, ): FilterableResult; }; From da34b6c3aede86336d8e669efc68f4b82602d90f Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 10:07:33 +0100 Subject: [PATCH 20/35] adds support for custom properties in FilterableResultMetadata --- queryable-spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/queryable-spec.ts b/queryable-spec.ts index 708cbf2..464e132 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -180,6 +180,11 @@ interface FilterableResultMetadata { * based on the provided pattern, expression and options. */ availableOrders?: QueryOperationOrder[]; + + /** + * Custom properties + */ + [key: string]: any; }; /** From ee78abb82bf09b8c16440ffd0633c688b4f79e1d Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 10:12:51 +0100 Subject: [PATCH 21/35] makes BindingsFactory#merge() return undefined on merge conflicts --- queryable-spec.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 464e132..ca6c299 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -310,7 +310,13 @@ interface BindingsFactory { bindings: (entries?: [RDF.Variable, RDF.Term][]) => Bindings; filter: (bindings: Bindings, fn: (value: RDF.Term, key: RDF.Variable) => boolean) => Bindings; map: (bindings: Bindings, fn: (value: RDF.Term, key: RDF.Variable) => RDF.Term) => Bindings; - merge: (left: Bindings, right: Bindings) => Bindings; + + /** + * Returns undefined in the presence of merge conflicts, that is when `left` + * and `right` both include a common variable (key) set to different terms + * (values). + */ + merge: (left: Bindings, right: Bindings) => Bindings | undefined; mergeWith: ( merger: (left: RDF.Term, right: RDF.Term, key: RDF.Variable) => RDF.Term, left: Bindings, From 8e7f57c73cb937bbe7db0f9e3fc5c16664038720 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 11:23:55 +0100 Subject: [PATCH 22/35] merges QueryableResultMetadata and FilterableResultMetadata, QueryableResult* and FilterableResult --- queryable-spec.ts | 252 ++++++++++++++++++++++------------------------ 1 file changed, 122 insertions(+), 130 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index ca6c299..d226824 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -65,6 +65,118 @@ interface QueryOperationOrder { terms: { term: T, direction: 'asc' | 'desc' }[]; } +interface QueryResultCardinality { + /** + * indicates the type of counting that was done, and MUST either be + * "estimate" or "exact". + */ + type: 'estimate' | 'exact'; + + /** + * Indicates an estimate of the number of quads in the stream if + * type = "estimate", or the exact number of quads in the stream if + * type = "exact". + */ + value: number; +} + +/** + * A QueryResultMetadata is an object that contains metadata about a certain + * query result. + */ +interface QueryResultMetadata { + + /** + * An optional field that contains metadata about the number of quads in the + * result stream. + */ + cardinality?: QueryResultCardinality; + + /** + * An optional field that contains the available options for quad sorting + * based on the provided pattern, expression and options. + */ + availableOrders?: QueryOperationOrder[]; + + /** + * Custom properties + */ + [key: string]: any; +} + +/** + * A QueryResultMetadataOptions is an object that gives suggestions on what + * type of metadata is desired, such as when invoking FilterResult.metadata. + */ + interface QueryResultMetadataOptions { + + /** + * optional field that MAY either contain "estimate" or "exact". If defined, + * this type MUST correspond to the type in QueryResultMetadataCardinality. + */ + cardinality?: 'estimate' | 'exact'; + + /** + * Custom properties + */ + [key: string]: any; +}; + +/** + * Generic interface that defines the API pattern for query result objects. + */ +interface BaseQueryResult { + + type: string; + + /** + * Returns either a stream containing all the items that match the given query, + * a boolean or void depending on the semantics of the given query. + */ + execute(opts?: any): Promise | boolean | void>; + + /** + * Asynchronously metadata of the current result. + */ + metadata?(opts?: QueryResultMetadataOptions): Promise>; + + /** + * Asynchronously returns a boolean indicating if the requested expression is + * supported. If it returns true, execute() and metadata() MAY produce valid + * results. If it returns false, execute() MUST return a stream emitting an + * error, and metadata() MUST reject. + */ + isSupported(): Promise; +} + + +interface QueryResultBindings extends BaseQueryResult { + type: 'bindings'; + execute(opts?: { order?: QueryOperationOrder }): Promise>; + variables: RDF.Variable[]; + metadata(opts: QueryResultMetadataOptions): Promise>; + isSupported(): Promise; +} + +interface QueryResultQuads extends BaseQueryResult { + type: 'quads'; + execute(opts?: { order?: QueryOperationOrder }): Promise>; + metadata(opts: QueryResultMetadataOptions): Promise>; + isSupported(): Promise; +} + +interface QueryResultBoolean extends BaseQueryResult { + type: 'boolean'; + execute(): Promise; + isSupported(): Promise; +} + +interface QueryResultVoid extends BaseQueryResult { + type: 'void'; + execute(): Promise; + isSupported(): Promise; +} + /****************************************************************************** FILTERABLE SOURCE @@ -143,93 +255,6 @@ interface ExpressionFactory { termExpression(term: RDF.Term): TermExpression; }; - -/** - * QueryResultMetadataCount is part of the QueryResultMetadata interface to - * represent metadata about the number of quads in the result stream. - */ -interface FilterableResultMetadataCardinality { - /** - * indicates the type of counting that was done, and MUST either be - * "estimate" or "exact". - */ - type: 'estimate' | 'exact'; - - /** - * Indicates an estimate of the number of quads in the stream if - * type = "estimate", or the exact number of quads in the stream if - * type = "exact". - */ - value: number; -}; - -/** - * A QueryResultMetadata is an object that contains metadata about a certain - * query result, such as invoking FilterableSource.matchExpression. - */ -interface FilterableResultMetadata { - - /** - * An optional field that contains metadata about the number of quads in the - * result stream. - */ - cardinality?: FilterableResultMetadataCardinality; - - /** - * An optional field that contains the available options for quad sorting - * based on the provided pattern, expression and options. - */ - availableOrders?: QueryOperationOrder[]; - - /** - * Custom properties - */ - [key: string]: any; -}; - -/** - * A QueryResultMetadataOptions is an object that gives suggestions on what - * type of metadata is desired, such as when invoking FilterResult.metadata. - */ -interface FilterableResultMetadataOptions { - - /** - * optional field that MAY either contain "estimate" or "exact". If defined, - * this type MUST correspond to the type in QueryResultMetadataCount. - */ - cardinality?: 'estimate' | 'exact'; -}; - - - -/** - * A FilterResult is an object that represents the result of a filter - * expression of FilterableSource for a given quad pattern and expression. - * It MAY create results lazily after one of its methods is invoked. - */ -interface FilterableResult { - - /** - * Returns a Stream containing all the quads that matched the given quad - * pattern and expression. - */ - execute(opts?: { order?: QueryOperationOrder }): Promise>; - - /** - * Asynchronously returns a QueryResultMetadata, that contains the metadata - * of the current result. - */ - metadata(opts?: FilterableResultMetadataOptions): Promise; - - /** - * Asynchronously returns a boolean indicating if the requested expression is - * supported by the FilterableSource. If it returns true, quads() and - * metadata() MAY produce a valid result. If it returns false, quads() MUST - * return a stream emitting an error, and metadata() MUST reject. - */ - isSupported(): Promise; -}; - /* * A FilterableSource is an object that produces a FilterableSourceResult that * can emit quads. The emitted quads can be directly contained in this @@ -273,7 +298,7 @@ interface FilterableSource { length?: number; start?: number; }, - ): FilterableResult; + ): QueryResultQuads; }; @@ -326,42 +351,9 @@ interface BindingsFactory { delete: (bindings: Bindings, key: RDF.Variable) => Bindings; } -/* - * Base interfaces to represent query results. These interfaces are generic - * with respect to the types of query metadata objects. These can be extended - * by implementors. - */ - -interface QueryableResultMetadata { - cardinality?: number; - availableOrders?: QueryOperationOrder[]; - [key: string]: any; -} - -interface QueryableResultBindings { - type: 'bindings'; - execute(opts?: { order?: QueryOperationOrder }): Promise>; - variables: RDF.Variable[]; - metadata(opts: { [key: string]: any }): Promise>; -} - -interface QueryableResultQuads { - type: 'quads'; - execute(opts?: { order?: QueryOperationOrder }): Promise>; - metadata(opts: { [key: string]: any }): Promise>; -} -interface QueryableResultBoolean { - type: 'boolean'; - execute(): Promise; -} - -interface QueryableResultVoid { - type: 'void'; - execute(): Promise; -} -type QueryableResult = QueryableResultBindings | QueryableResultBoolean | QueryableResultQuads | QueryableResultVoid; +type QueryableResult = QueryResultBindings | QueryResultBoolean | QueryResultQuads | QueryResultVoid; /* * Context objects provide a way to pass additional bits information to @@ -421,15 +413,15 @@ interface QueryableAlgebra { */ interface QueryableSparql { - boolean?(query: string, context?: QueryableStringContext): Promise; - bindings?(query: string, context?: QueryableContext): Promise; - quads?(query: string, context?: QueryableContext): Promise; - void?(query: string, context?: QueryableContext): Promise; + boolean?(query: string, context?: QueryableStringContext): Promise; + bindings?(query: string, context?: QueryableContext): Promise; + quads?(query: string, context?: QueryableContext): Promise; + void?(query: string, context?: QueryableContext): Promise; } interface QueryableAlgebraSparql { - boolean?(query: Algebra, context?: QueryableAlgebraContext): Promise; - bindings?(query: Algebra, context?: QueryableAlgebraContext): Promise; - quads?(query: Algebra, context?: QueryableAlgebraContext): Promise; - void?(query: Algebra, context?: QueryableContext): Promise; + boolean?(query: Algebra, context?: QueryableAlgebraContext): Promise; + bindings?(query: Algebra, context?: QueryableAlgebraContext): Promise; + quads?(query: Algebra, context?: QueryableAlgebraContext): Promise; + void?(query: Algebra, context?: QueryableContext): Promise; } From c24fb4783f4e8c7984b67b7fe17a2dee88026f8f Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 11:32:19 +0100 Subject: [PATCH 23/35] adds dedicated interface for execute() options --- queryable-spec.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index d226824..9ebac63 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -122,6 +122,19 @@ interface QueryResultMetadata { [key: string]: any; }; +interface QueryResultExecuteOptions { + + /** + * TBD + */ + order?: QueryOperationOrder; + + /** + * Custom properties + */ + [key: string]: any; +} + /** * Generic interface that defines the API pattern for query result objects. */ @@ -152,7 +165,7 @@ interface BaseQueryResult { interface QueryResultBindings extends BaseQueryResult { type: 'bindings'; - execute(opts?: { order?: QueryOperationOrder }): Promise>; + execute(opts?: QueryResultExecuteOptions): Promise>; variables: RDF.Variable[]; metadata(opts: QueryResultMetadataOptions): Promise>; isSupported(): Promise; @@ -160,7 +173,7 @@ interface QueryResultBindings extends BaseQueryResult { interface QueryResultQuads extends BaseQueryResult { type: 'quads'; - execute(opts?: { order?: QueryOperationOrder }): Promise>; + execute(opts?: QueryResultExecuteOptions): Promise>; metadata(opts: QueryResultMetadataOptions): Promise>; isSupported(): Promise; } From b1f26251b07451b996e30c201097ea2e627a418d Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 11:50:29 +0100 Subject: [PATCH 24/35] moves QueryResultBindings#variables into a dedicated interface extending QueryResultMetadata --- queryable-spec.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 9ebac63..f07752e 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -162,12 +162,14 @@ interface BaseQueryResult { isSupported(): Promise; } +interface QueryResultBindingsMetadata extends QueryResultMetadata { + variables: RDF.Variable[]; +} interface QueryResultBindings extends BaseQueryResult { type: 'bindings'; execute(opts?: QueryResultExecuteOptions): Promise>; - variables: RDF.Variable[]; - metadata(opts: QueryResultMetadataOptions): Promise>; + metadata(opts: QueryResultMetadataOptions): Promise; isSupported(): Promise; } From 63c2d9e010c84afa99653c38c777fb58e04e7eed Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 16:50:28 +0100 Subject: [PATCH 25/35] Replace the "Queryable" prefix with "Query" Co-authored-by: Ruben Taelman --- queryable-spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index f07752e..2c7b805 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -368,7 +368,7 @@ interface BindingsFactory { -type QueryableResult = QueryResultBindings | QueryResultBoolean | QueryResultQuads | QueryResultVoid; +type QueryResult = QueryResultBindings | QueryResultBoolean | QueryResultQuads | QueryResultVoid; /* * Context objects provide a way to pass additional bits information to @@ -382,20 +382,20 @@ type QueryableResult = QueryResultBindings | QueryResultBoolean | QueryResultQua // SourceType can be anything the query engine defines // TODO: we may consider defining some standards, like 'string', RDF.Source, ... -interface QueryableContext { +interface QueryContext { sources: [SourceType, ...SourceType[]]; queryTimestamp?: Date; // Required for certain SPARQL operations such as NOW(). [key: string]: any; } -interface QueryableStringContext extends QueryableContext { +interface QueryStringContext extends QueryableContext { queryFormat?: QueryableFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } baseIRI?: string; // Required for parsing SPARQL queries }; -interface QueryableAlgebraContext extends QueryableContext {}; +interface QueryAlgebraContext extends QueryableContext {}; -interface QueryableFormat { +interface QueryFormat { language: string; // Like 'SPARQL' version: string; // Like '1.1' extensions: string[]; // TODO: leave the syntax of these extensions open for now? From bba92c53b146f82c23023a564d6ce936363e4c3a Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 1 Dec 2021 16:53:15 +0100 Subject: [PATCH 26/35] Replace the "Queryable" prefix with "Query" --- queryable-spec.ts | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 2c7b805..4fb4528 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -388,12 +388,12 @@ interface QueryContext { [key: string]: any; } -interface QueryStringContext extends QueryableContext { - queryFormat?: QueryableFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } +interface QueryStringContext extends QueryContext { + queryFormat?: QueryFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] } baseIRI?: string; // Required for parsing SPARQL queries }; -interface QueryAlgebraContext extends QueryableContext {}; +interface QueryAlgebraContext extends QueryContext {}; interface QueryFormat { language: string; // Like 'SPARQL' @@ -414,12 +414,12 @@ type Algebra = any; * by engines such as Comunica. */ -interface Queryable { - query(query: string, context?: QueryableStringContext): Promise; +interface Queryable { + query(query: string, context?: QueryStringContext): Promise; } -interface QueryableAlgebra { - query(query: Algebra, context?: QueryableAlgebraContext): Promise; +interface QueryableAlgebra { + query(query: Algebra, context?: QueryAlgebraContext): Promise; } /* @@ -428,15 +428,15 @@ interface QueryableAlgebra { */ interface QueryableSparql { - boolean?(query: string, context?: QueryableStringContext): Promise; - bindings?(query: string, context?: QueryableContext): Promise; - quads?(query: string, context?: QueryableContext): Promise; - void?(query: string, context?: QueryableContext): Promise; + boolean?(query: string, context?: QueryStringContext): Promise; + bindings?(query: string, context?: QueryContext): Promise; + quads?(query: string, context?: QueryContext): Promise; + void?(query: string, context?: QueryContext): Promise; } interface QueryableAlgebraSparql { - boolean?(query: Algebra, context?: QueryableAlgebraContext): Promise; - bindings?(query: Algebra, context?: QueryableAlgebraContext): Promise; - quads?(query: Algebra, context?: QueryableAlgebraContext): Promise; - void?(query: Algebra, context?: QueryableContext): Promise; + boolean?(query: Algebra, context?: QueryAlgebraContext): Promise; + bindings?(query: Algebra, context?: QueryAlgebraContext): Promise; + quads?(query: Algebra, context?: QueryAlgebraContext): Promise; + void?(query: Algebra, context?: QueryContext): Promise; } From 8b7abcb0d63233409f1c68bbcf929e4a74759f3b Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Thu, 2 Dec 2021 13:56:34 +0100 Subject: [PATCH 27/35] renames QueryResult* to Query* --- queryable-spec.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 4fb4528..0fc7ad4 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -136,9 +136,9 @@ interface QueryResultExecuteOptions variables: RDF.Variable[]; } -interface QueryResultBindings extends BaseQueryResult { +interface QueryBindings extends BaseQuery { type: 'bindings'; execute(opts?: QueryResultExecuteOptions): Promise>; metadata(opts: QueryResultMetadataOptions): Promise; isSupported(): Promise; } -interface QueryResultQuads extends BaseQueryResult { +interface QueryQuads extends BaseQuery { type: 'quads'; execute(opts?: QueryResultExecuteOptions): Promise>; metadata(opts: QueryResultMetadataOptions): Promise>; isSupported(): Promise; } -interface QueryResultBoolean extends BaseQueryResult { +interface QueryBoolean extends BaseQuery { type: 'boolean'; execute(): Promise; isSupported(): Promise; } -interface QueryResultVoid extends BaseQueryResult { +interface QueryVoid extends BaseQuery { type: 'void'; execute(): Promise; isSupported(): Promise; @@ -313,7 +313,7 @@ interface FilterableSource { length?: number; start?: number; }, - ): QueryResultQuads; + ): QueryQuads; }; @@ -368,7 +368,7 @@ interface BindingsFactory { -type QueryResult = QueryResultBindings | QueryResultBoolean | QueryResultQuads | QueryResultVoid; +type Query = QueryBindings | QueryBoolean | QueryQuads | QueryVoid; /* * Context objects provide a way to pass additional bits information to @@ -414,11 +414,11 @@ type Algebra = any; * by engines such as Comunica. */ -interface Queryable { +interface Queryable { query(query: string, context?: QueryStringContext): Promise; } -interface QueryableAlgebra { +interface AlgebraQueryable { query(query: Algebra, context?: QueryAlgebraContext): Promise; } @@ -427,16 +427,16 @@ interface QueryableAlgebra { * objects are of the expected type as defined by the SPARQL spec. */ -interface QueryableSparql { - boolean?(query: string, context?: QueryStringContext): Promise; - bindings?(query: string, context?: QueryContext): Promise; - quads?(query: string, context?: QueryContext): Promise; - void?(query: string, context?: QueryContext): Promise; +interface SparqlQueryable { + boolean?(query: string, context?: QueryStringContext): Promise; + bindings?(query: string, context?: QueryStringContext): Promise; + quads?(query: string, context?: QueryStringContext): Promise; + void?(query: string, context?: QueryStringContext): Promise; } -interface QueryableAlgebraSparql { - boolean?(query: Algebra, context?: QueryAlgebraContext): Promise; - bindings?(query: Algebra, context?: QueryAlgebraContext): Promise; - quads?(query: Algebra, context?: QueryAlgebraContext): Promise; - void?(query: Algebra, context?: QueryContext): Promise; +interface SparqlAlgebraQueryable { + boolean?(query: Algebra, context?: QueryAlgebraContext): Promise; + bindings?(query: Algebra, context?: QueryAlgebraContext): Promise; + quads?(query: Algebra, context?: QueryAlgebraContext): Promise; + void?(query: Algebra, context?: QueryAlgebraContext): Promise; } From fc1d32a9fde5a23bea4d03a250022bbfd54721be Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 8 Dec 2021 16:23:47 +0100 Subject: [PATCH 28/35] drops isSupported() --- queryable-spec.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 0fc7ad4..c4bbdbf 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -152,14 +152,6 @@ interface BaseQuery { * Asynchronously metadata of the current result. */ metadata?(opts?: QueryResultMetadataOptions): Promise>; - - /** - * Asynchronously returns a boolean indicating if the requested expression is - * supported. If it returns true, execute() and metadata() MAY produce valid - * results. If it returns false, execute() MUST return a stream emitting an - * error, and metadata() MUST reject. - */ - isSupported(): Promise; } interface QueryResultBindingsMetadata extends QueryResultMetadata { @@ -170,26 +162,22 @@ interface QueryBindings extends BaseQuery { type: 'bindings'; execute(opts?: QueryResultExecuteOptions): Promise>; metadata(opts: QueryResultMetadataOptions): Promise; - isSupported(): Promise; } interface QueryQuads extends BaseQuery { type: 'quads'; execute(opts?: QueryResultExecuteOptions): Promise>; metadata(opts: QueryResultMetadataOptions): Promise>; - isSupported(): Promise; } interface QueryBoolean extends BaseQuery { type: 'boolean'; execute(): Promise; - isSupported(): Promise; } interface QueryVoid extends BaseQuery { type: 'void'; execute(): Promise; - isSupported(): Promise; } From 022adecbc51f282d203b1380d87d828ab319d5e1 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Wed, 8 Dec 2021 16:38:00 +0100 Subject: [PATCH 29/35] adds comments mentioning possible rejections in case of unsupported queries / expressions --- queryable-spec.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index c4bbdbf..1fc1f0f 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -291,6 +291,10 @@ interface ExpressionFactory { * applying the Expression filter. */ interface FilterableSource { + + /** + * May reject given an unsupported expression. + */ matchExpression( subject?: RDF.Term, predicate?: RDF.Term, @@ -301,7 +305,7 @@ interface FilterableSource { length?: number; start?: number; }, - ): QueryQuads; + ): Promise; }; @@ -403,10 +407,16 @@ type Algebra = any; */ interface Queryable { + /** + * May reject given an unsupported query. + */ query(query: string, context?: QueryStringContext): Promise; } interface AlgebraQueryable { + /** + * May reject given an unsupported query. + */ query(query: Algebra, context?: QueryAlgebraContext): Promise; } From 5e86528bcaa7cc6e881f99e49857824ed509fe2a Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Thu, 9 Dec 2021 21:28:36 +0100 Subject: [PATCH 30/35] renames type to resultType, refactors metadata to use specific methods --- queryable-spec.ts | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 1fc1f0f..9d23256 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -90,13 +90,13 @@ interface QueryResultMetadata { * An optional field that contains metadata about the number of quads in the * result stream. */ - cardinality?: QueryResultCardinality; + cardinality(precision?: 'estimate' | 'exact'): Promise; /** * An optional field that contains the available options for quad sorting * based on the provided pattern, expression and options. */ - availableOrders?: QueryOperationOrder[]; + availableOrders(): Promise[]>; /** * Custom properties @@ -104,24 +104,6 @@ interface QueryResultMetadata { [key: string]: any; } -/** - * A QueryResultMetadataOptions is an object that gives suggestions on what - * type of metadata is desired, such as when invoking FilterResult.metadata. - */ - interface QueryResultMetadataOptions { - - /** - * optional field that MAY either contain "estimate" or "exact". If defined, - * this type MUST correspond to the type in QueryResultMetadataCardinality. - */ - cardinality?: 'estimate' | 'exact'; - - /** - * Custom properties - */ - [key: string]: any; -}; - interface QueryResultExecuteOptions { /** @@ -140,7 +122,7 @@ interface QueryResultExecuteOptions>; + metadata?: QueryResultMetadata; } interface QueryResultBindingsMetadata extends QueryResultMetadata { - variables: RDF.Variable[]; + variables(): Promise; } interface QueryBindings extends BaseQuery { - type: 'bindings'; + resultType: 'bindings'; execute(opts?: QueryResultExecuteOptions): Promise>; - metadata(opts: QueryResultMetadataOptions): Promise; + metadata: QueryResultBindingsMetadata; } interface QueryQuads extends BaseQuery { - type: 'quads'; + resultType: 'quads'; execute(opts?: QueryResultExecuteOptions): Promise>; - metadata(opts: QueryResultMetadataOptions): Promise>; + metadata: QueryResultMetadata; } interface QueryBoolean extends BaseQuery { - type: 'boolean'; + resultType: 'boolean'; execute(): Promise; } interface QueryVoid extends BaseQuery { - type: 'void'; + resultType: 'void'; execute(): Promise; } From 1f6ca660e501f5a9db60f84c8280c1682fe63ff5 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 10 Dec 2021 09:28:09 +0100 Subject: [PATCH 31/35] refactors high-level *Queryable interfaces into a single interface using generics to declare support for specific result types --- queryable-spec.ts | 51 ++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 9d23256..6e87b37 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -136,14 +136,10 @@ interface BaseQuery { metadata?: QueryResultMetadata; } -interface QueryResultBindingsMetadata extends QueryResultMetadata { - variables(): Promise; -} - interface QueryBindings extends BaseQuery { resultType: 'bindings'; execute(opts?: QueryResultExecuteOptions): Promise>; - metadata: QueryResultBindingsMetadata; + metadata: QueryResultMetadata & { variables(): Promise; }; } interface QueryQuads extends BaseQuery { @@ -340,8 +336,6 @@ interface BindingsFactory { delete: (bindings: Bindings, key: RDF.Variable) => Bindings; } - - type Query = QueryBindings | QueryBoolean | QueryQuads | QueryVoid; /* @@ -388,18 +382,11 @@ type Algebra = any; * by engines such as Comunica. */ -interface Queryable { - /** - * May reject given an unsupported query. - */ - query(query: string, context?: QueryStringContext): Promise; -} - -interface AlgebraQueryable { +interface Queryable { /** * May reject given an unsupported query. */ - query(query: Algebra, context?: QueryAlgebraContext): Promise; + query(query: QueryFormatType, context?: QueryStringContext): Promise; } /* @@ -407,16 +394,22 @@ interface AlgebraQueryable { * objects are of the expected type as defined by the SPARQL spec. */ -interface SparqlQueryable { - boolean?(query: string, context?: QueryStringContext): Promise; - bindings?(query: string, context?: QueryStringContext): Promise; - quads?(query: string, context?: QueryStringContext): Promise; - void?(query: string, context?: QueryStringContext): Promise; -} - -interface SparqlAlgebraQueryable { - boolean?(query: Algebra, context?: QueryAlgebraContext): Promise; - bindings?(query: Algebra, context?: QueryAlgebraContext): Promise; - quads?(query: Algebra, context?: QueryAlgebraContext): Promise; - void?(query: Algebra, context?: QueryAlgebraContext): Promise; -} +type BindingsResult = { bindings: true }; +type VoidResult = { void: true }; +type QuadsResult = { quads: true }; +type BooleanResult = { boolean: true }; + +type SparqlQueryable = {} + & (SupportedResultType extends BindingsResult ? { + queryBindings(query: QueryFormatType, context?: QueryStringContext): Promise>; + } : {}) + & (SupportedResultType extends BooleanResult ? { + queryBoolean(query: QueryFormatType, context?: QueryStringContext): Promise; + } : {}) + & (SupportedResultType extends QuadsResult ? { + queryQuads(query: QueryFormatType, context?: QueryStringContext): Promise>; + } : {}) + & (SupportedResultType extends VoidResult ? { + queryVoid(query: QueryFormatType, context?: QueryStringContext): Promise; + } : {}) +; From a5fc74d5fce4fb6f43e5ed49bfd24cb72a45d485 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 10 Dec 2021 09:31:02 +0100 Subject: [PATCH 32/35] renames QueryResultExecuteOptions to QueryExecuteOptions --- queryable-spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 6e87b37..5efcc9e 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -104,7 +104,7 @@ interface QueryResultMetadata { [key: string]: any; } -interface QueryResultExecuteOptions { +interface QueryExecuteOptions { /** * TBD @@ -138,13 +138,13 @@ interface BaseQuery { interface QueryBindings extends BaseQuery { resultType: 'bindings'; - execute(opts?: QueryResultExecuteOptions): Promise>; + execute(opts?: QueryExecuteOptions): Promise>; metadata: QueryResultMetadata & { variables(): Promise; }; } interface QueryQuads extends BaseQuery { resultType: 'quads'; - execute(opts?: QueryResultExecuteOptions): Promise>; + execute(opts?: QueryExecuteOptions): Promise>; metadata: QueryResultMetadata; } From b12a29c576eb1d109c7ac4115cf496392e139794 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 28 Jan 2022 12:07:46 +0100 Subject: [PATCH 33/35] drops one layer of indirection when retrieving metadata --- queryable-spec.ts | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 5efcc9e..86562ee 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -84,25 +84,25 @@ interface QueryResultCardinality { * A QueryResultMetadata is an object that contains metadata about a certain * query result. */ -interface QueryResultMetadata { - - /** - * An optional field that contains metadata about the number of quads in the - * result stream. - */ - cardinality(precision?: 'estimate' | 'exact'): Promise; - +type CardinalityMetadataOpts = { cardinality: 'estimate' | 'exact'; }; +type AvailableOrdersMetadataOpts = { availableOrders: true; }; + +type QueryResultMetadata = { [key: string]: any } + & (MetadataOptsType extends CardinalityMetadataOpts ? { + /** + * An optional field that contains metadata about the number of quads in the + * result stream. + */ + cardinality: QueryResultCardinality, + } : {}) + & (MetadataOptsType extends AvailableOrdersMetadataOpts ? { /** * An optional field that contains the available options for quad sorting * based on the provided pattern, expression and options. */ - availableOrders(): Promise[]>; - - /** - * Custom properties - */ - [key: string]: any; -} + availableOrders: QueryOperationOrder[]; + } : {}) +; interface QueryExecuteOptions { @@ -129,23 +129,18 @@ interface BaseQuery { * a boolean or void depending on the semantics of the given query. */ execute(opts?: any): Promise | boolean | void>; - - /** - * Asynchronously metadata of the current result. - */ - metadata?: QueryResultMetadata; } interface QueryBindings extends BaseQuery { resultType: 'bindings'; execute(opts?: QueryExecuteOptions): Promise>; - metadata: QueryResultMetadata & { variables(): Promise; }; + metadata(opts?: M): Promise & { variables(): Promise; }>; } interface QueryQuads extends BaseQuery { resultType: 'quads'; execute(opts?: QueryExecuteOptions): Promise>; - metadata: QueryResultMetadata; + metadata(opts?: M): Promise>; } interface QueryBoolean extends BaseQuery { From 035741f716da8bc5f6051de4304870dfa1656768 Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 28 Jan 2022 14:40:57 +0100 Subject: [PATCH 34/35] fixes type resolution when dealing with metadata --- queryable-spec.ts | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index 86562ee..c61e5ca 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -84,25 +84,22 @@ interface QueryResultCardinality { * A QueryResultMetadata is an object that contains metadata about a certain * query result. */ -type CardinalityMetadataOpts = { cardinality: 'estimate' | 'exact'; }; -type AvailableOrdersMetadataOpts = { availableOrders: true; }; - -type QueryResultMetadata = { [key: string]: any } - & (MetadataOptsType extends CardinalityMetadataOpts ? { - /** - * An optional field that contains metadata about the number of quads in the - * result stream. - */ - cardinality: QueryResultCardinality, - } : {}) - & (MetadataOptsType extends AvailableOrdersMetadataOpts ? { - /** - * An optional field that contains the available options for quad sorting - * based on the provided pattern, expression and options. - */ - availableOrders: QueryOperationOrder[]; - } : {}) -; + +interface CardinalityMetadataOpts { + cardinality: 'estimate' | 'exact'; +}; + +interface AvailableOrdersMetadataOpts { + availableOrders: true; +}; + +interface BaseMetadataQuery { + metadata(opts?: M): Promise< + AdditionalMetadataType + & (M extends CardinalityMetadataOpts ? { cardinality: QueryResultCardinality } : {}) + & (M extends AvailableOrdersMetadataOpts ? { availableOrders: QueryOperationOrder[] } : {}) + >; +} interface QueryExecuteOptions { @@ -131,16 +128,14 @@ interface BaseQuery { execute(opts?: any): Promise | boolean | void>; } -interface QueryBindings extends BaseQuery { +interface QueryBindings extends BaseQuery, BaseMetadataQuery { resultType: 'bindings'; execute(opts?: QueryExecuteOptions): Promise>; - metadata(opts?: M): Promise & { variables(): Promise; }>; } -interface QueryQuads extends BaseQuery { +interface QueryQuads extends BaseQuery, BaseMetadataQuery { resultType: 'quads'; execute(opts?: QueryExecuteOptions): Promise>; - metadata(opts?: M): Promise>; } interface QueryBoolean extends BaseQuery { From e42a0fe342bf2ab1fb6528033f443dfaa823e16c Mon Sep 17 00:00:00 2001 From: Jacopo Scazzosi Date: Fri, 28 Jan 2022 15:40:53 +0100 Subject: [PATCH 35/35] execute()'s options do not need to include the selected ordering's cost --- queryable-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/queryable-spec.ts b/queryable-spec.ts index c61e5ca..aefce17 100644 --- a/queryable-spec.ts +++ b/queryable-spec.ts @@ -106,7 +106,7 @@ interface QueryExecuteOptions { /** * TBD */ - order?: QueryOperationOrder; + order?: QueryOperationOrder['terms']; /** * Custom properties