Skip to content

Commit bde15f4

Browse files
committed
fix: starting with an empty store
1 parent cfc5c04 commit bde15f4

10 files changed

Lines changed: 334 additions & 3 deletions

File tree

engines/config-query-sparql-incremental/config/query-operation/actors.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"ccqs:config/query-operation/actors/query/from.json",
1212
"icqsi:config/query-operation/actors/query/group.json",
1313
"icqsi:config/query-operation/actors/query/orderby.json",
14-
"ccqs:config/query-operation/actors/query/join.json",
14+
"icqsi:config/query-operation/actors/query/join.json",
1515
"ccqs:config/query-operation/actors/query/leftjoin.json",
1616
"ccqs:config/query-operation/actors/query/minus.json",
1717
"ccqs:config/query-operation/actors/query/nop.json",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"@context": [
3+
"https://linkedsoftwaredependencies.org/bundles/npm/@comunica/runner/^4.0.0/components/context.jsonld",
4+
5+
"https://linkedsoftwaredependencies.org/bundles/npm/@incremunica/actor-query-operation-join/^2.0.0/components/context.jsonld"
6+
],
7+
"@id": "urn:comunica:default:Runner",
8+
"@type": "Runner",
9+
"actors": [
10+
{
11+
"@id": "urn:comunica:default:query-operation/actors#join",
12+
"@type": "ActorQueryOperationJoin",
13+
"mediatorQueryOperation": { "@id": "urn:comunica:default:query-operation/mediators#main" },
14+
"mediatorJoin": { "@id": "urn:comunica:default:rdf-join/mediators#main" }
15+
}
16+
]
17+
}

engines/query-sparql-incremental/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@
179179
"@comunica/actor-query-operation-bgp-join": "^4.0.2",
180180
"@comunica/actor-query-operation-extend": "^4.0.2",
181181
"@comunica/actor-query-operation-from-quad": "^4.0.2",
182-
"@comunica/actor-query-operation-join": "^4.0.2",
183182
"@comunica/actor-query-operation-leftjoin": "^4.0.2",
184183
"@comunica/actor-query-operation-minus": "^4.0.2",
185184
"@comunica/actor-query-operation-nop": "^4.0.2",
@@ -269,6 +268,7 @@
269268
"@incremunica/actor-query-operation-distinct-hash": "^2.1.0",
270269
"@incremunica/actor-query-operation-filter": "^2.1.0",
271270
"@incremunica/actor-query-operation-group": "^2.1.0",
271+
"@incremunica/actor-query-operation-join": "^2.1.0",
272272
"@incremunica/actor-query-operation-orderby": "^2.1.0",
273273
"@incremunica/actor-query-operation-reduced-hash": "^2.1.0",
274274
"@incremunica/actor-query-operation-slice": "^2.1.0",

engines/query-sparql-incremental/test/QuerySparql-test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,30 @@ describe('System test: QuerySparql (without external network)', () => {
4040
streamingStore = new StreamingStore<Quad>();
4141
});
4242

43+
it('simple query that starts with empty store', async() => {
44+
const bindingStream = await engine.queryBindings(`
45+
SELECT ?s ?p1 ?o1 ?p2 ?o2 WHERE {
46+
?s ?p1 ?o1 .
47+
?o1 ?p2 ?o2 . }`, {
48+
sources: [ streamingStore ],
49+
});
50+
51+
streamingStore.addQuad(quad('s', 'p1', 'o1'));
52+
streamingStore.addQuad(quad('o1', 'p2', 'o2'));
53+
54+
await expect(partialArrayifyAsyncIterator(bindingStream, 1)).resolves.toBeIsomorphicBindingsArray([
55+
BF.bindings([
56+
[ DF.variable('s'), DF.namedNode('s') ],
57+
[ DF.variable('p1'), DF.namedNode('p1') ],
58+
[ DF.variable('o1'), DF.namedNode('o1') ],
59+
[ DF.variable('p2'), DF.namedNode('p2') ],
60+
[ DF.variable('o2'), DF.namedNode('o2') ],
61+
]).setContextEntry(KeysBindings.isAddition, true),
62+
]);
63+
64+
streamingStore.end();
65+
});
66+
4367
it('simple query with GROUPBY', async() => {
4468
streamingStore.addQuad(quad('Alice', 'http://test/hasInterest', 'Cooking'));
4569
streamingStore.addQuad(quad('Alice', 'http://test/hasInterest', 'Reading'));

packages/actor-query-operation-group/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Incremuncia Group Query Operation Actor
1+
# Incremunica Group Query Operation Actor
22

33
[![npm version](https://badge.fury.io/js/@incremunica%2Factor-query-operation-group.svg)](https://www.npmjs.com/package/@incremunica/actor-query-operation-group)
44

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Incremunica Join Query Operation Actor
2+
3+
[![npm version](https://badge.fury.io/js/@incremunica%2Factor-query-operation-join.svg)](https://www.npmjs.com/package/@incremunica/actor-query-operation-join)
4+
5+
A [Query Operation](https://github.com/comunica/comunica/tree/master/packages/bus-query-operation) actor that handles SPARQL Join operations
6+
by delegating to the [RDF Join bus](https://github.com/comunica/comunica/tree/master/packages/bus-rdf-join) bus.
7+
8+
## Install
9+
10+
```bash
11+
$ yarn add @incremunica/actor-query-operation-join
12+
```
13+
14+
## Configure
15+
16+
After installing, this package can be added to your engine's configuration as follows:
17+
```text
18+
{
19+
"@context": [
20+
...
21+
"https://linkedsoftwaredependencies.org/bundles/npm/@incremunica/actor-query-operation-join/^2.0.0/components/context.jsonld"
22+
],
23+
"actors": [
24+
...
25+
{
26+
"@id": "urn:comunica:default:query-operation/actors#join",
27+
"@type": "ActorQueryOperationJoin",
28+
"mediatorQueryOperation": { "@id": "urn:comunica:default:query-operation/mediators#main" },
29+
"mediatorJoin": { "@id": "urn:comunica:default:rdf-join/mediators#main" }
30+
}
31+
]
32+
}
33+
```
34+
35+
### Config Parameters
36+
37+
* `mediatorQueryOperation`: A mediator over the [Query Operation bus](https://github.com/comunica/comunica/tree/master/packages/bus-query-operation).
38+
* `mediatorJoin`: A mediator over the [RDF Join bus](https://github.com/comunica/comunica/tree/master/packages/bus-rdf-join).
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import type { IActorQueryOperationTypedMediatedArgs } from '@comunica/bus-query-operation';
2+
import {
3+
ActorQueryOperationTypedMediated,
4+
} from '@comunica/bus-query-operation';
5+
import type { MediatorRdfJoin } from '@comunica/bus-rdf-join';
6+
import type { IActorTest, TestResult } from '@comunica/core';
7+
import { passTestVoid } from '@comunica/core';
8+
import type { IQueryOperationResult, IActionContext, IJoinEntry } from '@comunica/types';
9+
import { getSafeBindings } from '@comunica/utils-query-operation';
10+
import type { Algebra } from 'sparqlalgebrajs';
11+
12+
/**
13+
* A comunica Join Query Operation Actor.
14+
*/
15+
export class ActorQueryOperationJoin extends ActorQueryOperationTypedMediated<Algebra.Join> {
16+
public readonly mediatorJoin: MediatorRdfJoin;
17+
18+
public constructor(args: IActorQueryOperationJoinArgs) {
19+
super(args, 'join');
20+
}
21+
22+
public async testOperation(_operation: Algebra.Join, _context: IActionContext): Promise<TestResult<IActorTest>> {
23+
return passTestVoid();
24+
}
25+
26+
public async runOperation(
27+
operationOriginal: Algebra.Join,
28+
context: IActionContext,
29+
): Promise<IQueryOperationResult> {
30+
const entries: IJoinEntry[] = (await Promise.all(operationOriginal.input
31+
.map(async subOperation => ({
32+
output: await this.mediatorQueryOperation.mediate({ operation: subOperation, context }),
33+
operation: subOperation,
34+
}))))
35+
.map(({ output, operation }) => ({
36+
output: getSafeBindings(output),
37+
operation,
38+
}));
39+
40+
return this.mediatorJoin.mediate({ type: 'inner', entries, context });
41+
}
42+
}
43+
44+
export interface IActorQueryOperationJoinArgs extends IActorQueryOperationTypedMediatedArgs {
45+
/**
46+
* A mediator for joining Bindings streams
47+
*/
48+
mediatorJoin: MediatorRdfJoin;
49+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './ActorQueryOperationJoin';
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "@incremunica/actor-query-operation-join",
3+
"version": "2.1.0",
4+
"description": "A join query-operation actor",
5+
"lsd:module": true,
6+
"license": "MIT",
7+
"homepage": "https://maartyman.github.io/incremunica/",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/maartyman/incremunica.git",
11+
"directory": "packages/actor-query-operation-join"
12+
},
13+
"bugs": {
14+
"url": "https://github.com/maartyman/incremunica/issues"
15+
},
16+
"keywords": [
17+
"incremunica",
18+
"actor",
19+
"query-operation",
20+
"join"
21+
],
22+
"sideEffects": false,
23+
"main": "lib/index.js",
24+
"typings": "lib/index",
25+
"publishConfig": {
26+
"access": "public"
27+
},
28+
"files": [
29+
"components",
30+
"lib/**/*.d.ts",
31+
"lib/**/*.js",
32+
"lib/**/*.js.map"
33+
],
34+
"scripts": {
35+
"build": "yarn run build:ts && yarn run build:components",
36+
"build:ts": "node \"../../node_modules/typescript/bin/tsc\"",
37+
"build:components": "componentsjs-generator"
38+
},
39+
"dependencies": {
40+
"@comunica/bus-query-operation": "^4.0.2",
41+
"@comunica/bus-rdf-join": "^4.0.2",
42+
"@comunica/core": "^4.0.2",
43+
"@comunica/types": "^4.0.2",
44+
"@comunica/utils-query-operation": "^4.0.2",
45+
"sparqlalgebrajs": "^4.3.8"
46+
}
47+
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { ActorQueryOperation } from '@comunica/bus-query-operation';
2+
import { ActionContext, Bus } from '@comunica/core';
3+
import type { IJoinEntry } from '@comunica/types';
4+
import { BindingsFactory } from '@comunica/utils-bindings-factory';
5+
import { MetadataValidationState } from '@comunica/utils-metadata';
6+
import { getSafeBindings } from '@comunica/utils-query-operation';
7+
import { ArrayIterator, UnionIterator } from 'asynciterator';
8+
import { DataFactory } from 'rdf-data-factory';
9+
import { ActorQueryOperationJoin } from '../lib/ActorQueryOperationJoin';
10+
import '@comunica/utils-jest';
11+
12+
const DF = new DataFactory();
13+
const BF = new BindingsFactory(DF);
14+
15+
describe('ActorQueryOperationJoin', () => {
16+
let bus: any;
17+
let mediatorQueryOperation: any;
18+
let mediatorJoin: any;
19+
20+
beforeEach(() => {
21+
bus = new Bus({ name: 'bus' });
22+
mediatorQueryOperation = {
23+
mediate: (arg: any) => Promise.resolve({
24+
bindingsStream: new ArrayIterator([
25+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
26+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
27+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
28+
], { autoStart: false }),
29+
metadata: () => Promise.resolve({
30+
state: new MetadataValidationState(),
31+
cardinality: { type: 'exact', value: 3 },
32+
variables: [{ variable: DF.variable('a'), canBeUndef: false }],
33+
}),
34+
operated: arg,
35+
type: 'bindings',
36+
}),
37+
};
38+
mediatorJoin = {
39+
mediate: (arg: any) => Promise.resolve({
40+
bindingsStream: new UnionIterator(arg.entries.map((entry: IJoinEntry) => entry.output.bindingsStream)),
41+
metadata: () => Promise.resolve({
42+
state: new MetadataValidationState(),
43+
cardinality: { type: 'exact', value: 100 },
44+
variables: [
45+
{ variable: DF.variable('a'), canBeUndef: false },
46+
{ variable: DF.variable('b'), canBeUndef: false },
47+
],
48+
}),
49+
operated: arg,
50+
type: 'bindings',
51+
}),
52+
};
53+
});
54+
55+
describe('The ActorQueryOperationJoin module', () => {
56+
it('should be a function', () => {
57+
expect(ActorQueryOperationJoin).toBeInstanceOf(Function);
58+
});
59+
60+
it('should be a ActorQueryOperationJoin constructor', () => {
61+
expect(new (<any> ActorQueryOperationJoin)({ name: 'actor', bus, mediatorQueryOperation, mediatorJoin }))
62+
.toBeInstanceOf(ActorQueryOperationJoin);
63+
expect(new (<any> ActorQueryOperationJoin)({ name: 'actor', bus, mediatorQueryOperation, mediatorJoin }))
64+
.toBeInstanceOf(ActorQueryOperation);
65+
});
66+
67+
it('should not be able to create new ActorQueryOperationJoin objects without \'new\'', () => {
68+
expect(() => {
69+
(<any> ActorQueryOperationJoin)();
70+
}).toThrow(`Class constructor ActorQueryOperationJoin cannot be invoked without 'new'`);
71+
});
72+
});
73+
74+
describe('An ActorQueryOperationJoin instance', () => {
75+
let actor: ActorQueryOperationJoin;
76+
77+
beforeEach(() => {
78+
actor = new ActorQueryOperationJoin({ name: 'actor', bus, mediatorQueryOperation, mediatorJoin });
79+
});
80+
81+
it('should test on join', async() => {
82+
const op: any = { operation: { type: 'join' }};
83+
await expect(actor.test(op)).resolves.toPassTestVoid();
84+
});
85+
86+
it('should not test on non-join', async() => {
87+
const op: any = { operation: { type: 'some-other-type' }};
88+
await expect(actor.test(op)).resolves.toFailTest(`Actor actor only supports join operations, but got some-other-type`);
89+
});
90+
91+
it('should run', async() => {
92+
const op: any = { operation: { type: 'join', input: [{}, {}, {}]}, context: new ActionContext() };
93+
const output = getSafeBindings(await actor.run(op, undefined));
94+
expect(output.type).toBe('bindings');
95+
await expect(output.metadata()).resolves.toEqual({
96+
state: expect.any(MetadataValidationState),
97+
cardinality: { type: 'exact', value: 100 },
98+
variables: [
99+
{ variable: DF.variable('a'), canBeUndef: false },
100+
{ variable: DF.variable('b'), canBeUndef: false },
101+
],
102+
});
103+
await expect(output.bindingsStream).toEqualBindingsStream([
104+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
105+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
106+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
107+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
108+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
109+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
110+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
111+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
112+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
113+
]);
114+
});
115+
116+
it('should run when one of the join entries has an estimated cardinality of 0', async() => {
117+
mediatorQueryOperation.mediate = (arg: any) => Promise.resolve({
118+
bindingsStream: new ArrayIterator([
119+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
120+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
121+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
122+
], { autoStart: false }),
123+
metadata: () => Promise.resolve({
124+
cardinality: { type: 'estimate', value: 0 },
125+
variables: [{ variable: DF.variable('a'), canBeUndef: false }],
126+
}),
127+
operated: arg,
128+
type: 'bindings',
129+
});
130+
131+
const op: any = { operation: { type: 'join', input: [{}, {}, {}]}, context: new ActionContext() };
132+
const output = getSafeBindings(await actor.run(op, undefined));
133+
expect(output.type).toBe('bindings');
134+
await expect(output.metadata()).resolves.toEqual({
135+
state: expect.any(MetadataValidationState),
136+
cardinality: { type: 'exact', value: 100 },
137+
variables: [
138+
{ variable: DF.variable('a'), canBeUndef: false },
139+
{ variable: DF.variable('b'), canBeUndef: false },
140+
],
141+
});
142+
await expect(output.bindingsStream).toEqualBindingsStream([
143+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
144+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
145+
BF.bindings([[ DF.variable('a'), DF.literal('1') ]]),
146+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
147+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
148+
BF.bindings([[ DF.variable('a'), DF.literal('2') ]]),
149+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
150+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
151+
BF.bindings([[ DF.variable('a'), DF.literal('3') ]]),
152+
]);
153+
});
154+
});
155+
});

0 commit comments

Comments
 (0)