Skip to content

Commit b2ebd9e

Browse files
committed
Test base units of modelToQuery (#455)
1 parent 9c72238 commit b2ebd9e

File tree

2 files changed

+203
-5
lines changed

2 files changed

+203
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { each } from 'lodash';
2+
3+
import { startStore, endStore } from '../test-util';
4+
import mockOntology from '../mock-data/mock-ontology';
5+
6+
import Model from '../core/model';
7+
import Collection from '../core/collection';
8+
import { rdf, rdfs, owl, xsd, readit } from '../common-rdf/ns';
9+
import Node from '../common-rdf/node';
10+
import Graph from '../common-rdf/graph';
11+
12+
import modelToQuery, {
13+
serializeIri,
14+
serializeLiteral,
15+
serializePredicate,
16+
serializeExpression,
17+
combineAnd,
18+
combineOr,
19+
} from './modelToQuery';
20+
21+
describe('semantic search query serialization', function() {
22+
beforeEach(startStore);
23+
beforeEach(function() {
24+
this.ontology = new Graph(mockOntology);
25+
});
26+
afterEach(endStore);
27+
28+
describe('serializeIri', function() {
29+
it('puts an IRI between angle brackets by default', function() {
30+
expect(serializeIri(rdfs.range, {})).toBe(`<${rdfs.range}>`);
31+
});
32+
33+
it('applies a namespace abbreviation when available', function() {
34+
expect(serializeIri(rdfs.range, { r: rdfs() })).toBe('r:range');
35+
});
36+
37+
it('ignores irrelevant namespaces', function() {
38+
expect(serializeIri(rdfs.range, {
39+
o: owl(),
40+
})).toBe(`<${rdfs.range}>`);
41+
expect(serializeIri(rdfs.range, {
42+
r: rdfs(),
43+
o: owl(),
44+
})).toBe('r:range');
45+
});
46+
});
47+
48+
describe('serializeLiteral', function() {
49+
it('serializes numbers', function() {
50+
expect(serializeLiteral('123', xsd.integer, {})).toBe('123');
51+
expect(serializeLiteral('12.3', xsd.decimal, {})).toBe('12.3');
52+
expect(serializeLiteral('.123', xsd.double, {})).toBe('.123');
53+
});
54+
55+
it('serializes strings', function() {
56+
expect(serializeLiteral('abc', xsd.string, {})).toBe('"abc"');
57+
});
58+
59+
it('serializes dates', function() {
60+
expect(
61+
serializeLiteral('2021-06-01', xsd.dateTime, {})
62+
).toBe(`"2021-06-01"^^<${xsd.dateTime}>`);
63+
expect(
64+
serializeLiteral('2021-06-01', xsd.dateTime, { x: xsd() })
65+
).toBe('"2021-06-01"^^x:dateTime');
66+
});
67+
});
68+
69+
describe('serializePredicate', function() {
70+
beforeEach(function() {
71+
this.property = this.ontology.find({'@type': rdf.Property});
72+
expect(this.property).toBeDefined();
73+
});
74+
75+
it('behaves like serializeIri for direct properties', function() {
76+
const pr = this.property;
77+
const id = pr.id;
78+
each([{}, {r: readit()}], function(ns) {
79+
expect(serializePredicate(pr, ns)).toBe(serializeIri(id, ns));
80+
});
81+
});
82+
83+
it('uses caret notation for inverse properties', function() {
84+
const pr = this.property;
85+
const id = pr.id;
86+
const inv = new Node({ [owl.inverseOf]: pr });
87+
each([{}, {r: readit()}], function(ns) {
88+
expect(serializePredicate(inv, ns))
89+
.toBe(`^${serializeIri(id, ns)}`);
90+
});
91+
});
92+
});
93+
94+
describe('serializeExpression', function() {
95+
it('supports binary operator expressions', function() {
96+
const filter = new Model({ operator: '+' });
97+
const args = ['1', '2'];
98+
expect(serializeExpression(filter, args).expression)
99+
.toBe('(1 + 2)');
100+
});
101+
102+
it('supports unary function calls', function() {
103+
const filter = new Model({ function: 'testme' });
104+
const args = ['"abc"'];
105+
expect(serializeExpression(filter, args).expression)
106+
.toBe('testme("abc")');
107+
});
108+
109+
it('supports binary function calls', function() {
110+
const filter = new Model({ function: 'testme' });
111+
const args = ['1', '2'];
112+
expect(serializeExpression(filter, args).expression)
113+
.toBe('testme(1, 2)');
114+
});
115+
});
116+
117+
describe('combineAnd', function() {
118+
it('joins expressions', function() {
119+
expect(combineAnd({ expression: [{
120+
tag: 'expression',
121+
expression: '(1 < 2)',
122+
}, {
123+
tag: 'expression',
124+
expression: '(3 < 4)',
125+
}]})).toEqual({
126+
tag: 'expression',
127+
expression: '((1 < 2) && (3 < 4))',
128+
});
129+
});
130+
131+
it('joins patterns', function() {
132+
expect(combineAnd({ pattern: [{
133+
tag: 'pattern',
134+
pattern: '?a ?b ?c.\n',
135+
}, {
136+
tag: 'pattern',
137+
pattern: '?d ?e ?f.\n',
138+
}]})).toEqual({
139+
tag: 'pattern',
140+
pattern: '?a ?b ?c.\n?d ?e ?f.\n',
141+
});
142+
});
143+
144+
it('joins mixtures of patterns and expressions', function() {
145+
expect(combineAnd({ expression: [{
146+
tag: 'expression',
147+
expression: '(1 < 2)',
148+
}], pattern: [{
149+
tag: 'pattern',
150+
pattern: '?a ?b ?c.\n',
151+
}]})).toEqual({
152+
tag: 'pattern',
153+
pattern: '?a ?b ?c.\nFILTER (1 < 2)\n',
154+
});
155+
});
156+
});
157+
158+
describe('combineOr', function() {
159+
it('joins expressions', function() {
160+
expect(combineOr({ expression: [{
161+
tag: 'expression',
162+
expression: '(1 < 2)',
163+
}, {
164+
tag: 'expression',
165+
expression: '(3 < 4)',
166+
}]})).toEqual({
167+
tag: 'expression',
168+
expression: '((1 < 2) || (3 < 4))',
169+
});
170+
});
171+
172+
it('joins patterns', function() {
173+
expect(combineOr({ pattern: [{
174+
tag: 'pattern',
175+
pattern: '?a ?b ?c.\n',
176+
}, {
177+
tag: 'pattern',
178+
pattern: '?d ?e ?f.\n',
179+
}]})).toEqual({
180+
tag: 'pattern',
181+
pattern: '{\n?a ?b ?c.\n} UNION {\n?d ?e ?f.\n}\n',
182+
});
183+
});
184+
185+
it('joins mixtures of patterns and expressions', function() {
186+
expect(combineOr({ expression: [{
187+
tag: 'expression',
188+
expression: '(1 < 2)',
189+
}], pattern: [{
190+
tag: 'pattern',
191+
pattern: '?a ?b ?c.\n',
192+
}]})).toEqual({
193+
tag: 'expression',
194+
expression: '((1 < 2) || EXISTS {\n?a ?b ?c.\n})',
195+
});
196+
});
197+
});
198+
});

frontend/src/semantic-search/modelToQuery.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export function serializeIri(iri: string, ns: nsTable): string {
5050
return short || `<${iri}>`;
5151
}
5252

53-
function serializeLiteral(
53+
export function serializeLiteral(
5454
literal: string, datatype: string, ns: nsTable
5555
): string {
5656
switch (datatype) {
@@ -66,7 +66,7 @@ function nextVariable(): string {
6666
return uniqueId('?x');
6767
}
6868

69-
function serializePredicate(predicate: Node, ns: nsTable): string {
69+
export function serializePredicate(predicate: Node, ns: nsTable): string {
7070
const inverse = predicate.get(owl.inverseOf) as Node[];
7171
if (inverse && inverse.length) return `^${serializeIri(inverse[0].id, ns)}`;
7272
return serializeIri(predicate.id, ns);
@@ -84,7 +84,7 @@ function tagPattern(pattern: string): TaggedPattern {
8484
return { tag: 'pattern', pattern };
8585
}
8686

87-
function serializeExpression(filter: Model, args: string[]): TaggedExpression {
87+
export function serializeExpression(filter: Model, args: string[]): TaggedExpression {
8888
const func = filter.get('function') || '';
8989
const op = filter.get('operator');
9090
const sep = op ? ` ${op} ` : ', ';
@@ -103,7 +103,7 @@ function joinTagged<K extends keyof Branches>(key: K) {
103103
const joinE = joinTagged('expression');
104104
const joinP = joinTagged('pattern');
105105

106-
function combineAnd({ expression, pattern }: Branches): TaggedSyntax {
106+
export function combineAnd({ expression, pattern }: Branches): TaggedSyntax {
107107
const exp = expression ? `(${joinE(expression, ' && ')})` : '';
108108
const pat = pattern ? joinP(pattern, '') : '';
109109
if (exp) {
@@ -113,7 +113,7 @@ function combineAnd({ expression, pattern }: Branches): TaggedSyntax {
113113
return tagPattern(pat);
114114
}
115115

116-
function combineOr({ expression, pattern }: Branches): TaggedSyntax {
116+
export function combineOr({ expression, pattern }: Branches): TaggedSyntax {
117117
if (expression) {
118118
const patExp = pattern ? map(pattern, patternAsExpression) : [];
119119
return tagExpression(`${joinE(expression.concat(patExp), ' || ')}`);

0 commit comments

Comments
 (0)