-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqueryable-spec.ts
405 lines (350 loc) · 13 KB
/
queryable-spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
import RDF from '@rdfjs/types';
import { EventEmitter } from 'events'; // TODO: refer to underlying interface, not to the class
/******************************************************************************
COMMON INTERFACES AND TYPES
*****************************************************************************/
/*
* 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<Q> 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;
/**
* Custom properties
*/
[key: string]: any;
}
/**
* 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<T extends TermName | RDF.Variable> {
cost: QueryOperationCost;
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 CardinalityMetadataOpts {
cardinality: 'estimate' | 'exact';
};
interface AvailableOrdersMetadataOpts {
availableOrders: true;
};
interface BaseMetadataQuery<OrderItemsType extends TermName | RDF.Variable, AdditionalMetadataType extends {}> {
metadata<M extends CardinalityMetadataOpts | AvailableOrdersMetadataOpts>(opts?: M): Promise<
AdditionalMetadataType
& (M extends CardinalityMetadataOpts ? { cardinality: QueryResultCardinality } : {})
& (M extends AvailableOrdersMetadataOpts ? { availableOrders: QueryOperationOrder<OrderItemsType>[] } : {})
>;
}
interface QueryExecuteOptions<OrderItemsType extends TermName | RDF.Variable> {
/**
* TBD
*/
order?: QueryOperationOrder<OrderItemsType>['terms'];
/**
* Custom properties
*/
[key: string]: any;
}
/**
* Generic interface that defines the API pattern for query objects.
*/
interface BaseQuery {
resultType: 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<Stream<any> | boolean | void>;
}
interface QueryBindings extends BaseQuery, BaseMetadataQuery<RDF.Variable, { variables: RDF.Variable[] }> {
resultType: 'bindings';
execute(opts?: QueryExecuteOptions<RDF.Variable>): Promise<Stream<Bindings>>;
}
interface QueryQuads extends BaseQuery, BaseMetadataQuery<TermName, {}> {
resultType: 'quads';
execute(opts?: QueryExecuteOptions<TermName>): Promise<Stream<RDF.Quad>>;
}
interface QueryBoolean extends BaseQuery {
resultType: 'boolean';
execute(): Promise<boolean>;
}
interface QueryVoid extends BaseQuery {
resultType: 'void';
execute(): Promise<void>;
}
/******************************************************************************
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;
};
/*
* 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 {
/**
* May reject given an unsupported expression.
*/
matchExpression(
subject?: RDF.Term,
predicate?: RDF.Term,
obj?: RDF.Term,
graph?: RDF.Term,
expression?: Expression,
opts?: {
length?: number;
start?: number;
},
): Promise<QueryQuads>;
};
/******************************************************************************
QUERYABLE SOURCE
*****************************************************************************/
/*
* Map-like representation of Bindings as using plain objects could lead
* 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 extends Iterable<[RDF.Variable, RDF.Term]> {
type: 'bindings';
has: (key: RDF.Variable) => boolean;
get: (key: RDF.Variable) => RDF.Term | undefined;
keys: () => Iterator<RDF.Variable>;
values: () => Iterator<RDF.Term>;
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]>;
}
/*
* 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;
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;
/**
* 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,
right: Bindings,
) => Bindings;
set: (bindings: Bindings, key: RDF.Variable, value: RDF.Term) => Bindings;
delete: (bindings: Bindings, key: RDF.Variable) => Bindings;
}
type Query = QueryBindings | QueryBoolean | QueryQuads | QueryVoid;
/*
* 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<SourceType> {
sources: [SourceType, ...SourceType[]];
queryTimestamp?: Date; // Required for certain SPARQL operations such as NOW().
[key: string]: any;
}
interface QueryStringContext<SourceType> extends QueryContext<SourceType> {
queryFormat?: QueryFormat; // defaults to { language: 'SPARQL', version: '1.1', extensions: [] }
baseIRI?: string; // Required for parsing SPARQL queries
};
interface QueryAlgebraContext<SourceType> extends QueryContext<SourceType> {};
interface QueryFormat {
language: string; // Like 'SPARQL'
version: string; // Like '1.1'
extensions: string[]; // TODO: leave the syntax of these extensions open for now?
}
/**
* Placeholder to represent SPARQL Algebra trees.
* Algebra typings are TBD. Reference implementations include:
* - https://www.npmjs.com/package/sparqlalgebrajs
*/
type Algebra = any;
/*
* 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<QueryFormatType extends string | Algebra, SourceType, QueryType extends Query> {
/**
* May reject given an unsupported query.
*/
query(query: QueryFormatType, context?: QueryStringContext<SourceType>): Promise<QueryType>;
}
/*
* SPARQL-constrainted query interfaces. These interfaces guarantee that result
* objects are of the expected type as defined by the SPARQL spec.
*/
type BindingsResult = { bindings: true };
type VoidResult = { void: true };
type QuadsResult = { quads: true };
type BooleanResult = { boolean: true };
type SparqlQueryable<QueryFormatType extends string | Algebra, SourceType, SupportedResultType> = {}
& (SupportedResultType extends BindingsResult ? {
queryBindings(query: QueryFormatType, context?: QueryStringContext<SourceType>): Promise<Stream<Bindings>>;
} : {})
& (SupportedResultType extends BooleanResult ? {
queryBoolean(query: QueryFormatType, context?: QueryStringContext<SourceType>): Promise<boolean>;
} : {})
& (SupportedResultType extends QuadsResult ? {
queryQuads(query: QueryFormatType, context?: QueryStringContext<SourceType>): Promise<Stream<RDF.Quad>>;
} : {})
& (SupportedResultType extends VoidResult ? {
queryVoid(query: QueryFormatType, context?: QueryStringContext<SourceType>): Promise<void>;
} : {})
;