Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add queryable interfaces #30

Merged
merged 21 commits into from
Feb 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b962f59
Add initial draft interfaces for the query spec
rubensworks Jan 26, 2022
e30f83b
Move all mutability methods to Bindings
rubensworks Jan 26, 2022
e2d62fc
Tweak queryable interfaces based on implementation tests
rubensworks Feb 3, 2022
bc7163e
Add changelog for addition of queryable interfaces
rubensworks Feb 3, 2022
5577666
Enter prerelease mode and version packages
rubensworks Feb 3, 2022
4e5bafb
Make GitHub CI also publish prereleases from feature/query branch
rubensworks Feb 3, 2022
71ee4ea
Allow supported metadata types to be overridden
rubensworks Feb 4, 2022
1a2e38e
Add TODO to merge Stream types
rubensworks Feb 4, 2022
60ff070
Release 1.1.0-next.1
rubensworks Feb 4, 2022
f64f002
Temporarily remove filterable typings
rubensworks Feb 10, 2022
d32e71c
Also allow Bindings interaction via strings
rubensworks Feb 14, 2022
2b8f571
Fix typo in SparqlQueryable tsdoc
rubensworks Feb 14, 2022
762a892
Add tests for queryable interface
rubensworks Feb 14, 2022
3b7ba40
modularizes Queryable and SparqlQueryable interfaces
jacoscaz Feb 12, 2022
61f5a6a
mentions the difference between queries provided as strings and queri…
jacoscaz Feb 14, 2022
d7ba23c
adds support for multiple algebra types, drops conceptual dependency …
jacoscaz Feb 14, 2022
a666eab
updates test to use modularized interfaces
jacoscaz Feb 14, 2022
186c2b7
adds tests for Algebra interfaces
jacoscaz Feb 14, 2022
d47c8b1
drops redundant semicolons
jacoscaz Feb 14, 2022
567ae66
Merge pull request #32 from jacoscaz/feature/query-separate-interfaces
rubensworks Feb 15, 2022
09ad631
Exit prerelease mode
rubensworks Feb 17, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/long-files-look.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rdfjs/types": patch
---

Make queryable metadata types configurable
13 changes: 13 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha! This will be the real last comment. Should the pre take be removed or are you intending another prerelease?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, will remove.

"mode": "exit",
"tag": "next",
"initialVersions": {
"@rdfjs/types": "1.0.1"
},
"changesets": [
"friendly-lies-suffer",
"long-files-look",
"seven-shrimps-build",
"tough-guests-flash"
]
}
5 changes: 5 additions & 0 deletions .changeset/tough-guests-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rdfjs/types": minor
---

Add queryable interfaces
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- master
- feature/query

jobs:
release:
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# @rdfjs/types

## 1.1.0-next.1

### Patch Changes

- Make queryable metadata types configurable

## 1.1.0-next.0

### Minor Changes

- 95f1e31: Dataset: Use correct type of `dataset` in methods with callbacks
- bc7163e: Add queryable interfaces

### Patch Changes

- 8164183: Documentation Fix: Update reference of Quad to BaseQuad in the definition of Term in order to align with the type declaration.

## 1.0.1

### Patch Changes
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
export * from './data-model';
export * from './stream';
export * from './dataset';
export * from './query';
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rdfjs/types",
"version": "1.0.1",
"version": "1.1.0-next.1",
"license": "MIT",
"types": "index.d.ts",
"author": {
Expand Down
7 changes: 7 additions & 0 deletions query.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* Query Interfaces */
/* https://rdf.js.org/query-spec/ */

export * from './query/common';
export * from './query/queryable';


287 changes: 287 additions & 0 deletions query/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
/* Query Interfaces - Common */
/* https://rdf.js.org/query-spec/ */

import { EventEmitter } from "events";
import * as RDF from '../data-model';

/**
* Helper union type for quad term names.
*/
export type QuadTermName = 'subject' | 'predicate' | 'object' | 'graph';

// TODO: merge this with Stream upon the next major change
/**
* Custom typings for the RDF/JS ResultStream interface as the current
* typings restrict the generic param Q to extensions of "BaseQuad",
* meaning it cannot be used for Bindings.
*/
export interface ResultStream<Q> extends EventEmitter {
read(): Q | null;
jacoscaz marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* QueryOperationCost represents the cost of a given query operation.
*/
export 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 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.
*
* These objects can represent orderings of both quad and bindings streams,
* respectively identified by quad term names and variables.
*/
export interface QueryOperationOrder<T extends QuadTermName | RDF.Variable> {
cost: QueryOperationCost;
terms: { term: T, direction: 'asc' | 'desc' }[];
}

/**
* QueryResultCardinality represents the number of results, which can either be an estimate or exact value.
*/
export interface QueryResultCardinality {
/**
* indicates the type of counting that was done, and MUST either be "estimate" or "exact".
*/
type: 'estimate' | 'exact';

/**
* Indicates an estimated of the number of results in the stream if type = "estimate",
* or the exact number of results in the stream if type = "exact".
*/
value: number;
}

/**
* BaseMetadataQuery is helper interface that provides a metadata callback.
*/
interface BaseMetadataQuery<OrderItemsType extends QuadTermName | RDF.Variable, AdditionalMetadataType extends unknown, SupportedMetadataType> {
/**
* Asynchronously return metadata of the current result.
*/
metadata<M extends MetadataOpts<SupportedMetadataType>>(opts?: M): Promise<ConditionalMetadataType<AdditionalMetadataType, M, OrderItemsType>>;
}

export type AllMetadataSupport = CardinalityMetadataSupport & OrderMetadataSupport & AvailableOrdersMetadataSupport;
export type CardinalityMetadataSupport = { cardinality: true };
export type OrderMetadataSupport = { order: true };
export type AvailableOrdersMetadataSupport = { availableOrders: true };

export type MetadataOpts<SupportedMetadataType> =
(SupportedMetadataType extends CardinalityMetadataSupport ? CardinalityMetadataOpts : unknown) |
(SupportedMetadataType extends OrderMetadataSupport ? OrderMetadataOpts : unknown) |
(SupportedMetadataType extends AvailableOrdersMetadataSupport ? AvailableOrdersMetadataOpts : unknown);
export interface CardinalityMetadataOpts { cardinality: 'estimate' | 'exact'; }
export interface OrderMetadataOpts { order: true; }
export interface AvailableOrdersMetadataOpts { availableOrders: true; }

export type ConditionalMetadataType<AdditionalMetadataType, M, OrderItemsType extends QuadTermName | RDF.Variable> = AdditionalMetadataType
& (M extends CardinalityMetadataOpts ? { cardinality: QueryResultCardinality } : Record<string, unknown>)
& (M extends OrderMetadataOpts ? { order: QueryOperationOrder<OrderItemsType>['terms'] } : Record<string, unknown>)
& (M extends AvailableOrdersMetadataOpts ? { availableOrders: QueryOperationOrder<OrderItemsType>[] } : Record<string, unknown>);

jacoscaz marked this conversation as resolved.
Show resolved Hide resolved
/**
* Options that can be passed when executing a query.
*/
export interface QueryExecuteOptions<OrderItemsType extends QuadTermName | RDF.Variable> {
/**
* The required order for the result stream.
*/
order?: QueryOperationOrder<OrderItemsType>;

/**
* Custom properties
*/
[key: string]: any;
}

/**
* Generic interface that defines query objects following the Future pattern.
*/
export interface BaseQuery {
/**
* Identifier for the type of result of tis query.
*/
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<ResultStream<any> | boolean | void>;
}

/**
* Query object that returns bindings.
*/
export interface QueryBindings<SupportedMetadataType> extends BaseQuery, BaseMetadataQuery<RDF.Variable, { variables: RDF.Variable[] }, SupportedMetadataType> {
resultType: 'bindings';
execute(opts?: QueryExecuteOptions<RDF.Variable>): Promise<ResultStream<Bindings>>;
}

/**
* Query object that returns quads.
*/
export interface QueryQuads<SupportedMetadataType> extends BaseQuery, BaseMetadataQuery<QuadTermName, unknown, SupportedMetadataType> {
resultType: 'quads';
execute(opts?: QueryExecuteOptions<QuadTermName>): Promise<ResultStream<RDF.Quad>>;
}

/**
* Query object that returns a boolean.
*/
export interface QueryBoolean extends BaseQuery {
resultType: 'boolean';
execute(): Promise<boolean>;
}

/**
* Query object that returns void.
*/
export interface QueryVoid extends BaseQuery {
resultType: 'void';
execute(): Promise<void>;
}

/**
* Union type for the different query types.
*/
export type Query<SupportedMetadataType> = QueryBindings<SupportedMetadataType> | QueryBoolean | QueryQuads<SupportedMetadataType> | QueryVoid;

/**
* Bindings represents the mapping of variables to RDF values using an immutable Map-like representation.
* This means that methods such as `set` and `delete` do not modify this instance,
* but they return a new Bindings instance that contains the modification.
*
* Bindings instances are created using a BindingsFactory.
*
* The internal order of variable-value entries is undefined.
*/
export interface Bindings extends Iterable<[RDF.Variable, RDF.Term]> {
rubensworks marked this conversation as resolved.
Show resolved Hide resolved
type: 'bindings';
/**
* Check if a binding exist for the given variable.
* @param key A variable term or string. If it is a string, no `?` prefix must be given.
*/
has: (key: RDF.Variable | string) => boolean;
/**
* Obtain the binding value for the given variable.
* @param key A variable term or string. If it is a string, no `?` prefix must be given.
*/
get: (key: RDF.Variable | string) => RDF.Term | undefined;
/**
* Create a new Bindings object by adding the given variable and value mapping.
*
* If the variable already exists in the binding, then the existing mapping is overwritten.
*
* @param key The variable key term or string. If it is a string, no `?` prefix must be given.
* @param value The value.
*/
set: (key: RDF.Variable | string, value: RDF.Term) => Bindings;
/**
* Create a new Bindings object by removing the given variable.
*
* If the variable does not exist in the binding, a copy of the Bindings object is returned.
*
* @param key The variable key term or string. If it is a string, no `?` prefix must be given.
*/
delete: (key: RDF.Variable | string) => Bindings;
/**
* Obtain all variables for which mappings exist.
*/
keys: () => Iterable<RDF.Variable>;
/**
* Obtain all values that are mapped to.
*/
values: () => Iterable<RDF.Term>;
/**
* Iterate over all variable-value pairs.
* @param fn A callback that is called for each variable-value pair
* with value as first argument, and variable as second argument.
*/
forEach: (fn: (value: RDF.Term, key: RDF.Variable) => any) => void;
/**
* The number of variable-value pairs.
*/
size: number;
/**
* Iterator over all variable-value pairs.
*/
[Symbol.iterator]: () => Iterator<[RDF.Variable, RDF.Term]>;
/**
* Check if all entries contained in this Bindings object are equal to all entries in the other Bindings object.
* @param other A Bindings object.
*/
equals(other: Bindings | null | undefined): boolean;
/**
* Create a new Bindings object by filtering entries using a callback.
* @param fn A callback that is applied on each entry.
* Returning true indicates that this entry must be contained in the resulting Bindings object.
*/
filter: (fn: (value: RDF.Term, key: RDF.Variable) => boolean) => Bindings;
/**
* Create a new Bindings object by mapping entries using a callback.
* @param fn A callback that is applied on each entry, in which the original value is replaced by the returned value.
*/
map: (fn: (value: RDF.Term, key: RDF.Variable) => RDF.Term) => Bindings;
/**
* Merge this bindings with another.
*
* If a merge conflict occurs (this and other have an equal variable with unequal value),
* then undefined is returned.
*
* @param other A Bindings object.
*/
merge: (other: Bindings) => Bindings | undefined;
/**
* Merge this bindings with another, where merge conflicts can be resolved using a callback function.
* @param merger A function that is invoked when a merge conflict occurs,
* for which the returned value is considered the merged value.
* @param other A Bindings object.
*/
mergeWith: (
merger: (self: RDF.Term, other: RDF.Term, key: RDF.Variable) => RDF.Term,
other: Bindings,
) => Bindings;
}

/**
* BindingsFactory can create new instances of Bindings.
*/
export interface BindingsFactory {
/**
* Create a new Bindings object from the given variable-value entries.
* @param entries An array of entries, where each entry is a tuple containing a variable and a term.
*/
bindings: (entries?: [RDF.Variable, RDF.Term][]) => Bindings;

/**
* Create a copy of the given bindings object using this factory.
* @param bindings A Bindings object.
*/
fromBindings: (bindings: Bindings) => Bindings;
}
Loading