Skip to content

Commit

Permalink
Merge pull request #99 from maartyman/add-optional
Browse files Browse the repository at this point in the history
Added incremental optional operator
  • Loading branch information
maartyman authored Apr 23, 2024
2 parents c85d00c + 8308dda commit aea11b6
Show file tree
Hide file tree
Showing 9 changed files with 1,218 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"icqsi:config/query-operation/actors/query/quadpattern.json",
"ccqs:config/query-operation/actors/query/union.json",
"ccqs:config/query-operation/actors/query/minus.json",
"ccqs:config/query-operation/actors/query/leftjoin.json",
"ccqs:config/query-operation/actors/query/values.json",

"ccqs:config/query-operation/actors/query/path-alt.json",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
{
"@context": [
"https://linkedsoftwaredependencies.org/bundles/npm/@comunica/runner/^2.0.0/components/context.jsonld"
"https://linkedsoftwaredependencies.org/bundles/npm/@comunica/runner/^2.0.0/components/context.jsonld",

"https://linkedsoftwaredependencies.org/bundles/npm/@incremunica/actor-rdf-join-incremental-optional-hash/^1.0.0/components/context.jsonld"
],
"@id": "urn:comunica:default:Runner",
"@type": "Runner",
"actors": [

{
"@id": "urn:comunica:default:rdf-join/actors#incremental-optional-hash",
"@type": "ActorRdfJoinIncrementalOptionalHash",
"mediatorJoinSelectivity": { "@id": "urn:comunica:default:rdf-join-selectivity/mediators#main" }
}
]
}
Empty file.
31 changes: 31 additions & 0 deletions packages/actor-rdf-join-incremental-optional-hash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Incremunica Incremental Optional Hash RDF Join Actor

[![npm version](https://badge.fury.io/js/@incremunica%2Factor-rdf-join-incremental-optional-hash.svg)](https://badge.fury.io/js/@incremunica%2Factor-rdf-join-incremental-optional-hash)

An Incremunica Optional Hash RDF Join Actor.

## Install

```bash
$ yarn add @incremunica/actor-rdf-join-incremental-optional-hash
```

## Configure

After installing, this package can be added to your engine's configuration as follows:
```text
{
"@context": [
...
"https://linkedsoftwaredependencies.org/bundles/npm/@incremunica/actor-rdf-join-incremental-optional-hash/^1.0.0/components/context.jsonld"
],
"actors": [
...
{
"@id": "urn:comunica:default:rdf-join/actors#incremental-optional-hash",
"@type": "ActorRdfJoinIncrementalOptionalHash",
"mediatorJoinSelectivity": { "@id": "urn:comunica:default:rdf-join-selectivity/mediators#main" }
}
]
}
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { IActionRdfJoin, IActorRdfJoinArgs, IActorRdfJoinOutputInner } from '@comunica/bus-rdf-join';
import { ActorRdfJoin } from '@comunica/bus-rdf-join';
import type { IMediatorTypeJoinCoefficients } from '@comunica/mediatortype-join-coefficients';
import type { MetadataBindings } from '@comunica/types';
import type { BindingsStream } from '@incremunica/incremental-types';
import { IncrementalOptionalHash } from './IncrementalOptionalHash';

/**
* An Incremunica Optional Hash RDF Join Actor.
*/
export class ActorRdfJoinIncrementalOptionalHash extends ActorRdfJoin {
public constructor(args: IActorRdfJoinArgs) {
super(args, {
logicalType: 'optional',
physicalName: 'hash',
limitEntries: 2,
canHandleUndefs: false,
});
}

protected async getOutput(action: IActionRdfJoin): Promise<IActorRdfJoinOutputInner> {
const metadatas = await ActorRdfJoin.getMetadatas(action.entries);
const variables = ActorRdfJoin.overlappingVariables(metadatas);
const join = new IncrementalOptionalHash(
<BindingsStream><any>action.entries[0].output.bindingsStream,
<BindingsStream><any>action.entries[1].output.bindingsStream,
entry => ActorRdfJoinIncrementalOptionalHash.hash(entry, variables),
<any> ActorRdfJoin.joinBindings,
);
return {
result: {
type: 'bindings',
bindingsStream: join,
metadata: async() => await this.constructResultMetadata(
action.entries,
await ActorRdfJoin.getMetadatas(action.entries),
action.context,
),
},
};
}

protected async getJoinCoefficients(
action: IActionRdfJoin,
metadatas: MetadataBindings[],
): Promise<IMediatorTypeJoinCoefficients> {
return {
iterations: 0,
persistedItems: 0,
blockingItems: 0,
requestTime: 0,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import type { Bindings as BindingsFactoryBindings } from '@incremunica/incremental-bindings-factory';
import { BindingsFactory } from '@incremunica/incremental-bindings-factory';
import { IncrementalInnerJoin } from '@incremunica/incremental-inner-join';
import type { Bindings, BindingsStream } from '@incremunica/incremental-types';

export class IncrementalOptionalHash extends IncrementalInnerJoin {
private readonly rightMemory: Map<string, Bindings[]> = new Map<string, Bindings[]>();
private readonly leftMemory: Map<string, Bindings[]> = new Map<string, Bindings[]>();
private activeElement: Bindings | null = null;
private otherArray: Bindings[] = [];
private index = 0;
private readonly funHash: (entry: Bindings) => string;
private prependArray: boolean;
private appendArray: boolean;
private readonly bindingsFactory = new BindingsFactory();

public constructor(
left: BindingsStream,
right: BindingsStream,
funHash: (entry: Bindings) => string,
funJoin: (...bindings: Bindings[]) => Bindings | null,
) {
super(left, right, funJoin);
this.funHash = funHash;
}

protected _cleanup(): void {
this.leftMemory.clear();
this.rightMemory.clear();
this.activeElement = null;
}

protected hasResults(): boolean {
return !this.leftIterator.done ||
!this.rightIterator.done ||
this.activeElement !== null;
}

private addOrDeleteFromMemory(item: Bindings, hash: string, memory: Map<string, Bindings[]>): boolean {
let array = memory.get(hash);
if (item.diff) {
if (array === undefined) {
array = [];
memory.set(hash, array);
}
array.push(item);
return true;
}

if (array === undefined) {
return false;
}

if (array.length < 2 && array[0].equals(item)) {
memory.delete(hash);
return true;
}

const index = array.findIndex((bindings: Bindings) => item.equals(bindings));
if (index !== -1) {
array[index] = array[array.length - 1];
array.pop();
return true;
}
return false;
}

public read(): Bindings | null {
// eslint-disable-next-line no-constant-condition
while (true) {
if (this.ended) {
return null;
}

// There is an active element
if (this.activeElement !== null || this.appendArray) {
if (this.index === this.otherArray.length) {
if (this.prependArray) {
this.prependArray = false;
this.index = 0;
continue;
}
if (this.appendArray && this.activeElement !== null) {
this.index = 0;
this.activeElement = null;
continue;
}
this.appendArray = false;
this.index = 0;
this.activeElement = null;
continue;
}

let resultingBindings = null;
if (this.prependArray) {
// We need to delete the bindings with no optional bindings
resultingBindings = this.bindingsFactory.fromBindings(<BindingsFactoryBindings> this.otherArray[this.index]);
resultingBindings.diff = false;
} else if (this.activeElement === null) {
// If this.activeElement is null, then appendArray is true
// we need to add the bindings with no optional bindings
resultingBindings = this.bindingsFactory.fromBindings(<BindingsFactoryBindings> this.otherArray[this.index]);
resultingBindings.diff = true;
} else {
// Otherwise merge bindings
resultingBindings = this.funJoin(this.activeElement, this.otherArray[this.index]);
}

this.index++;

if (resultingBindings !== null) {
return resultingBindings;
}
continue;
}

if (!this.hasResults()) {
this._end();
}

let item = this.rightIterator.read();
if (item !== null) {
const hash = this.funHash(item);
const rightMemEl = this.rightMemory.get(hash);
if (this.addOrDeleteFromMemory(item, hash, this.rightMemory)) {
const otherArray = this.leftMemory.get(hash);
if (otherArray !== undefined) {
if (item.diff && (rightMemEl === undefined || rightMemEl.length === 0)) {
this.prependArray = true;
}
if (!item.diff && this.rightMemory.get(hash)?.length === 1) {
this.appendArray = true;
}
this.activeElement = item;
this.otherArray = otherArray;
}
}
continue;
}

item = this.leftIterator.read();
if (item !== null) {
const hash = this.funHash(item);
if (this.addOrDeleteFromMemory(item, hash, this.leftMemory)) {
const otherArray = this.rightMemory.get(hash);
if (otherArray !== undefined) {
this.activeElement = item;
this.otherArray = otherArray;
} else {
return item;
}
}
continue;
}

this.readable = false;
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ActorRdfJoinIncrementalOptionalHash';
47 changes: 47 additions & 0 deletions packages/actor-rdf-join-incremental-optional-hash/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "@incremunica/actor-rdf-join-incremental-optional-hash",
"version": "1.2.2",
"description": "An incremental-optional-hash rdf-join actor",
"lsd:module": true,
"main": "lib/index.js",
"typings": "lib/index",
"repository": {
"type": "git",
"url": "https://github.com/maartyman/incremunica.git",
"directory": "packages/actor-rdf-join-incremental-optional-hash"
},
"publishConfig": {
"access": "public"
},
"sideEffects": false,
"keywords": [
"comunica",
"actor",
"rdf-join",
"incremental-optional-hash"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/maartyman/incremunica/issues"
},
"homepage": "https://maartyman.github.io/incremunica/",
"files": [
"components",
"lib/**/*.d.ts",
"lib/**/*.js",
"lib/**/*.js.map"
],
"dependencies": {
"@comunica/bus-rdf-join": "^2.10.1",
"@comunica/mediatortype-join-coefficients": "^2.10.0",
"@comunica/types": "^2.10.0",
"@incremunica/incremental-bindings-factory": "^1.2.2",
"@incremunica/incremental-inner-join": "^1.2.2",
"@incremunica/incremental-types": "^1.2.2"
},
"scripts": {
"build": "npm run build:ts && npm run build:components",
"build:ts": "node \"../../node_modules/typescript/bin/tsc\"",
"build:components": "componentsjs-generator"
}
}
Loading

0 comments on commit aea11b6

Please sign in to comment.