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 filterable interfaces #31

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
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 @@
{
"mode": "pre",
"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
8 changes: 8 additions & 0 deletions query.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* Query Interfaces */
/* https://rdf.js.org/query-spec/ */

export * from './query/common';
export * from './query/filterable';
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;
}

/**
* 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>);

/**
* 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]> {
type: 'bindings';
/**
* Check if a binding exist for the given variable.
* @param key A variable
*/
has: (key: RDF.Variable) => boolean;
/**
* Obtain the binding value for the given variable.
* @param key A variable
*/
get: (key: RDF.Variable) => 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.
* @param value The value.
*/
set: (key: RDF.Variable, 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.
*/
delete: (key: RDF.Variable) => 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