Skip to content

Commit a477980

Browse files
author
Soroush Saffari
authored
Supports Grakn Core 1.8 (#298)
## What is the goal of this PR? This PR introduces the changes required to support Grakn 1.8. Addinitially, users are now notified via an error notification, when they try to explain an inferred concept, whose corresponding rule definition contains at least one unassigned relation within the rule's `when` body. This PR also marks the `1.3.0` release of Workbase. ## What are the changes implemented in this PR? resolves #290 Following graknlabs/client-nodejs@b85668f - added query option `{ explain: true }` - `dataType` to `valueType` - `datatype` to `value` - `date` to `datetime` - removing all references to `isImplicit` - bump `client-nodejs` and `grakn-core` dependencies
1 parent c686ffe commit a477980

File tree

20 files changed

+560
-498
lines changed

20 files changed

+560
-498
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.2.10
1+
1.3.0

dependencies/graknlabs/dependencies.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ def graknlabs_grakn_core():
2929
git_repository(
3030
name = "graknlabs_grakn_core",
3131
remote = "https://github.com/graknlabs/grakn",
32-
tag = "1.7.2", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_grakn_core
32+
tag = "1.8.0", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_grakn_core
3333
)
3434

3535
def graknlabs_client_nodejs():
3636
git_repository(
3737
name = "graknlabs_client_nodejs",
3838
remote = "https://github.com/graknlabs/client-nodejs",
39-
tag = "1.7.0", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_client_nodejs
39+
tag = "1.8.0", # sync-marker: do not remove this comment, this is used for sync-dependencies by @graknlabs_client_nodejs
4040
)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"@blueprintjs/table": "^3.1.1",
5252
"codemirror": "^5.38.0",
5353
"electron-store": "^1.3.0",
54-
"grakn-client": "1.7.0",
54+
"grakn-client": "1.8.0",
5555
"grpc": "^1.18.0",
5656
"hex-to-hsl": "^1.0.2",
5757
"image-data-uri": "^1.1.1",
@@ -165,5 +165,5 @@
165165
"prebuild": "node version.js",
166166
"unit": "node ./node_modules/jest/bin/jest.js ./test/unit"
167167
},
168-
"version": "1.2.10"
168+
"version": "1.3.0"
169169
}

src/renderer/components/SchemaDesign/LeftBar/NewAttributePanel.vue

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
<div class="row">
2525
<div class="data-type-options">
2626
<div class="list-label">data type</div>
27-
<div v-if="superType === 'attribute'" class="btn data-type-btn" :class="(showDataTypeList) ? 'type-list-shown' : ''" @click="toggleDataList"><div class="type-btn-text" >{{dataType}}</div><div class="type-btn-caret"><vue-icon className="vue-icon" icon="caret-down"></vue-icon></div></div>
28-
<div v-else class="inherited-data-type">{{dataType}}</div>
27+
<div v-if="superType === 'attribute'" class="btn data-type-btn" :class="(showDataTypeList) ? 'type-list-shown' : ''" @click="toggleDataList"><div class="type-btn-text" >{{valueType}}</div><div class="type-btn-caret"><vue-icon className="vue-icon" icon="caret-down"></vue-icon></div></div>
28+
<div v-else class="inherited-data-type">{{valueType}}</div>
2929

3030
<div class="data-type-list" v-show="showDataTypeList">
31-
<ul v-for="type in dataTypes" :key=type>
32-
<li class="type-item" @click="selectDataType(type)" :class="[(type === dataType) ? 'type-item-selected' : '']">{{type}}</li>
31+
<ul v-for="type in valueTypes" :key=type>
32+
<li class="type-item" @click="selectDataType(type)" :class="[(type === valueType) ? 'type-item-selected' : '']">{{type}}</li>
3333
</ul>
3434
</div>
3535
</div>
@@ -371,8 +371,8 @@
371371
superTypes: [],
372372
superType: undefined,
373373
showDataTypeList: false,
374-
dataTypes: ['string', 'long', 'double', 'boolean', 'date'],
375-
dataType: undefined,
374+
valueTypes: ['string', 'long', 'double', 'boolean', 'datetime'],
375+
valueType: undefined,
376376
showSpinner: false,
377377
toggledAttributeTypes: [],
378378
toggledRoleTypes: [],
@@ -411,15 +411,15 @@
411411
if (val !== 'attribute') { // if super type is not 'attribute' set data type of super type
412412
const graknTx = await this[OPEN_GRAKN_TX]();
413413
const attributeType = await graknTx.getSchemaConcept(val);
414-
this.dataType = (await attributeType.dataType()).toLowerCase();
414+
this.valueType = (await attributeType.valueType()).toLowerCase();
415415
this.showDataTypeList = false;
416416
417417
const sup = await graknTx.getSchemaConcept(val);
418418
this.supAttributes = await Promise.all((await (await sup.attributes()).collect()).map(async x => x.label()));
419419
this.hasAttributes = this.hasAttributes.filter(x => !this.supAttributes.includes(x));
420420
graknTx.close();
421421
} else {
422-
this.dataType = this.dataTypes[0];
422+
this.valueType = this.valueTypes[0];
423423
424424
this.hasAttributes = this.metaTypeInstances.attributes;
425425
this.supAttributes = [];
@@ -433,7 +433,7 @@
433433
} else {
434434
this.showSpinner = true;
435435
this[DEFINE_ATTRIBUTE_TYPE]({
436-
attributeLabel: this.attributeLabel, superType: this.superType, dataType: this.dataType, attributeTypes: this.toggledAttributeTypes, roleTypes: this.toggledRoleTypes,
436+
attributeLabel: this.attributeLabel, superType: this.superType, valueType: this.valueType, attributeTypes: this.toggledAttributeTypes, roleTypes: this.toggledRoleTypes,
437437
})
438438
.then(() => {
439439
this.showSpinner = false;
@@ -452,7 +452,7 @@
452452
this.showAttributeTypeList = false;
453453
},
454454
selectDataType(type) {
455-
this.dataType = type;
455+
this.valueType = type;
456456
this.showDataTypeList = false;
457457
},
458458
resetPanel() {
@@ -461,7 +461,7 @@
461461
this.showDataTypeList = false;
462462
this.superTypes = ['attribute', ...this.metaTypeInstances.attributes];
463463
this.superType = this.superTypes[0];
464-
this.dataType = this.dataTypes[0];
464+
this.valueType = this.valueTypes[0];
465465
this.toggledAttributeTypes = [];
466466
this.toggledRoleTypes = [];
467467
this.showHasPanel = false;

src/renderer/components/SchemaDesign/RightBar/ConceptInfoTab/AttributesPanel.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
<div v-for="(value, index) in attributes" :key="index">
1414
<div class="content-item">
15-
<div class="label">{{value.type}}: {{value.dataType}}</div>
15+
<div class="label">{{value.type}}: {{value.valueType}}</div>
1616
<div class="btn right-bar-btn reset-setting-btn" @click="removeAttributeType(value.type, index)"><vue-icon icon="trash" className="vue-icon" iconSize="12"></vue-icon></div>
1717
</div>
1818
</div>

src/renderer/components/SchemaDesign/SchemaHandler.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
import { dataType } from 'grakn-client';
1+
import { valueType } from 'grakn-client';
22

33
let tx;
44

55
function SchemaHandler(graknTx) {
66
tx = graknTx;
77
}
88

9-
function toGraknDatatype(dataTypeParam) {
10-
switch (dataTypeParam) {
11-
case 'string': return dataType.STRING;
12-
case 'date': return dataType.DATETIME;
13-
case 'boolean': return dataType.BOOLEAN;
14-
case 'long': return dataType.LONG;
15-
case 'double': return dataType.DOUBLE;
16-
default: throw new Error(`Datatype not recognised. Received [${dataTypeParam}]`);
9+
function toGraknDatatype(valueTypeParam) {
10+
switch (valueTypeParam) {
11+
case 'string': return valueType.STRING;
12+
case 'datetime': return valueType.DATETIME;
13+
case 'boolean': return valueType.BOOLEAN;
14+
case 'long': return valueType.LONG;
15+
case 'double': return valueType.DOUBLE;
16+
default: throw new Error(`Datatype not recognised. Received [${valueTypeParam}]`);
1717
}
1818
}
1919

@@ -37,8 +37,8 @@ SchemaHandler.prototype.defineRelationType = async function define({ relationLab
3737
return type;
3838
};
3939

40-
SchemaHandler.prototype.defineAttributeType = async function define({ attributeLabel, superType, dataType }) {
41-
const type = await tx.putAttributeType(attributeLabel, toGraknDatatype(dataType));
40+
SchemaHandler.prototype.defineAttributeType = async function define({ attributeLabel, superType, valueType }) {
41+
const type = await tx.putAttributeType(attributeLabel, toGraknDatatype(valueType));
4242
const directSuper = await tx.getSchemaConcept(superType);
4343
await type.sup(directSuper);
4444
};

src/renderer/components/SchemaDesign/SchemaUtils.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ export async function loadMetaTypeInstances(graknTx) {
2727
.then(labels => labels.filter(l => l !== 'entity')
2828
.concat()
2929
.sort());
30-
metaTypeInstances.relations = await Promise.all(rels.map(async type => ((!await type.isImplicit()) ? type.label() : null)))
30+
metaTypeInstances.relations = await Promise.all(rels.map(async type => type.label()))
3131
.then(labels => labels.filter(l => l && l !== 'relation')
3232
.concat()
3333
.sort());
3434
metaTypeInstances.attributes = await Promise.all(attributes.map(type => type.label()))
3535
.then(labels => labels.filter(l => l !== 'attribute')
3636
.concat()
3737
.sort());
38-
metaTypeInstances.roles = await Promise.all(roles.map(async type => ((!await type.isImplicit()) ? type.label() : null)))
38+
metaTypeInstances.roles = await Promise.all(roles.map(async type => type.label()))
3939
.then(labels => labels.filter(l => l && l !== 'role')
4040
.concat()
4141
.sort());
@@ -47,7 +47,7 @@ export async function computeAttributes(nodes, graknTx) {
4747
return Promise.all(nodes.map(async (node) => {
4848
const concept = await graknTx.getSchemaConcept(node.label);
4949
const attributes = await (await concept.attributes()).collect();
50-
node.attributes = await Promise.all(attributes.map(async concept => ({ type: await concept.label(), dataType: await concept.dataType() })));
50+
node.attributes = await Promise.all(attributes.map(async concept => ({ type: await concept.label(), valueType: await concept.valueType() })));
5151
return node;
5252
}));
5353
}
@@ -57,7 +57,7 @@ export async function computeRoles(nodes, graknTx) {
5757
return Promise.all(nodes.map(async (node) => {
5858
const concept = await graknTx.getSchemaConcept(node.label);
5959
const roles = await (await concept.playing()).collect();
60-
node.roles = await Promise.all(roles.map(async concept => ((await concept.isImplicit()) ? null : concept.label()))).then(nodes => nodes.filter(x => x));
60+
node.roles = await Promise.all(roles.map(concept => concept.label())).then(nodes => nodes.filter(x => x));
6161
return node;
6262
}));
6363
}

src/renderer/components/Visualiser/TopBar/GraqlEditor/GraqlCodeMirror.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ CodeMirror.defineSimpleMode('graql', {
77
start: [
88
{ regex: /#.*/, token: 'comment' },
99
{ regex: /".*?"/, token: 'string' },
10-
{ regex: /(match|insert|delete|select|isa|sub|plays|relates|datatype|abstract|has|value|id|of|limit|offset|order|by|compute|from|to|in|aggregate|label|get|using|where)(?![-a-zA-Z_0-9])/, // eslint-disable-line max-len
10+
{ regex: /(match|isa|isa!|sub|sub!|has|id|type|limit|offset|sort|asc|desc|get|compute|path|from|to)(?![-a-zA-Z_0-9])/, // eslint-disable-line max-len
1111
token: 'keyword' },
1212
{ regex: /true|false/, token: 'number' },
1313
{ regex: /\$[-a-zA-Z_0-9]+/, token: 'variable' },

src/renderer/components/Visualiser/VisualiserUtils.js

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,15 @@ export async function loadMetaTypeInstances(graknTx) {
8787
.then(labels => labels.filter(l => l !== 'entity')
8888
.concat()
8989
.sort());
90-
metaTypeInstances.relations = await Promise.all(rels.map(async type => ((!await type.isImplicit()) ? type.label() : null)))
90+
metaTypeInstances.relations = await Promise.all(rels.map(type => type.label()))
9191
.then(labels => labels.filter(l => l && l !== 'relation')
9292
.concat()
9393
.sort());
9494
metaTypeInstances.attributes = await Promise.all(attributes.map(type => type.label()))
9595
.then(labels => labels.filter(l => l !== 'attribute')
9696
.concat()
9797
.sort());
98-
metaTypeInstances.roles = await Promise.all(roles.map(async type => ((!await type.isImplicit()) ? type.label() : null)))
98+
metaTypeInstances.roles = await Promise.all(roles.map(type => type.label()))
9999
.then(labels => labels.filter(l => l && l !== 'role')
100100
.concat()
101101
.sort());
@@ -128,27 +128,6 @@ export function addResetGraphListener(dispatch, action) {
128128
});
129129
}
130130

131-
/**
132-
* Checks if a ConceptMap inside the provided Answer contains at least one implicit concept
133-
* @param {Object} answer ConceptMap Answer to be inspected
134-
* @return {Boolean}
135-
*/
136-
async function answerContainsImplicitType(answer) {
137-
const concepts = Array.from(answer.map().values());
138-
return Promise.all(concepts.map(async concept => ((concept.isThing()) ? (await concept.type()).isImplicit() : concept.isImplicit())))
139-
.then(a => a.includes(true));
140-
}
141-
142-
/**
143-
* Filters out Answers that contained inferred concepts in their ConceptMap
144-
* @param {Object[]} answers array of ConceptMap Answers to be inspected
145-
* @return {Object[]} filtered array of Answers
146-
*/
147-
export async function filterMaps(answers) { // Filter out ConceptMaps that contain implicit relations
148-
return Promise.all(answers.map(async x => ((await answerContainsImplicitType(x)) ? null : x)))
149-
.then(maps => maps.filter(map => map));
150-
}
151-
152131
/**
153132
* Executes query to load neighbours of given node and filters our all the answers that contain implicit concepts, given that we
154133
* don't want to show implicit concepts (relations to attributes) to the user, for now.
@@ -159,11 +138,5 @@ export async function filterMaps(answers) { // Filter out ConceptMaps that conta
159138
export async function getFilteredNeighbourAnswers(node, graknTx, limit) {
160139
const query = getNeighboursQuery(node, limit);
161140
const resultAnswers = await (await graknTx.query(query)).collect();
162-
const filteredResult = await filterMaps(resultAnswers);
163-
if (resultAnswers.length !== filteredResult.length) {
164-
const offsetDiff = resultAnswers.length - filteredResult.length;
165-
node.offset += QuerySettings.getNeighboursLimit();
166-
return filteredResult.concat(await getFilteredNeighbourAnswers(node, graknTx, offsetDiff));
167-
}
168141
return resultAnswers;
169142
}

src/renderer/components/Visualiser/store/actions.js

Lines changed: 53 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export default {
117117
validateQuery(query);
118118
commit('loadingQuery', true);
119119
const graknTx = global.graknTx[rootState.activeTab];
120-
const result = await (await graknTx.query(query)).collect();
120+
const result = await (await graknTx.query(query, { explain: true })).collect();
121121
if (!result.length) {
122122
commit('loadingQuery', false);
123123
return null;
@@ -223,42 +223,62 @@ export default {
223223
const queryPattern = node.queryPattern;
224224
const queryPatternVariales = Array.from(new Set(queryPattern.match(/\$[^\s|)|;|,]*/g))).map(x => x.substring(1));
225225

226-
const explanationAnswers = (await node.explanation()).getAnswers();
227-
228-
const explanationPromises = [];
229-
const explanationResult = [];
230-
231-
explanationAnswers.forEach((answer) => {
232-
const answerVariabes = Array.from(answer.map().keys());
226+
const explanation = await node.explanation();
233227

234-
const isJointExplanation = answerVariabes.every(variable => queryPatternVariales.includes(variable));
235-
if (answer.hasExplanation() && isJointExplanation) {
236-
explanationPromises.push(answer.explanation());
237-
} else if (!isJointExplanation) {
238-
explanationResult.push(answer);
228+
const when = await (await explanation.getRule()).getWhen();
229+
const relRegex = /(\$[^\s]*|;|{)(\s*?\(.*?\))/g;
230+
let isRelUnassigned = false;
231+
let relMatches = relRegex.exec(when);
232+
while (relMatches) {
233+
if (!relMatches[1].includes('$')) {
234+
isRelUnassigned = true;
235+
break;
239236
}
240-
});
241-
242-
(await Promise.all(explanationPromises)).map(expl => expl.getAnswers()).reduce(collect, []).forEach((expl) => {
243-
explanationResult.push(expl);
244-
});
245-
246-
if (explanationResult.length > 0) {
247-
const data = await CDB.buildInstances(explanationResult);
248-
const rpData = await CDB.buildRPInstances(explanationResult, data, false, graknTx);
249-
data.nodes.push(...rpData.nodes);
250-
data.edges.push(...rpData.edges);
251-
252-
state.visFacade.addToCanvas(data);
253-
commit('updateCanvasData');
254-
const nodesWithAttributes = await computeAttributes(data.nodes, graknTx);
237+
relMatches = relRegex.exec(when);
238+
}
255239

256-
state.visFacade.updateNode(nodesWithAttributes);
257-
const styledEdges = data.edges.map(edge => ({ ...edge, label: edge.hiddenLabel, ...state.visStyle.computeExplanationEdgeStyle() }));
258-
state.visFacade.updateEdge(styledEdges);
259-
commit('loadingQuery', false);
240+
if (isRelUnassigned) {
241+
commit(
242+
'setGlobalErrorMsg',
243+
'The rule `when` definition for this inferred concept contains at least one unassigned relation. At the moment explanation cannot be provided for such a rule.',
244+
);
260245
} else {
261-
commit('setGlobalErrorMsg', 'The transaction has been refreshed since the loading of this node and, as a result, the explaination is incomplete.');
246+
const explanationAnswers = explanation.getAnswers();
247+
const explanationPromises = [];
248+
const explanationResult = [];
249+
250+
explanationAnswers.forEach((answer) => {
251+
const answerVariabes = Array.from(answer.map().keys());
252+
253+
const isJointExplanation = answerVariabes.every(variable => queryPatternVariales.includes(variable));
254+
if (answer.hasExplanation() && isJointExplanation) {
255+
explanationPromises.push(answer.explanation());
256+
} else if (!isJointExplanation) {
257+
explanationResult.push(answer);
258+
}
259+
});
260+
261+
(await Promise.all(explanationPromises)).map(expl => expl.getAnswers()).reduce(collect, []).forEach((expl) => {
262+
explanationResult.push(expl);
263+
});
264+
265+
if (explanationResult.length > 0) {
266+
const data = await CDB.buildInstances(explanationResult);
267+
const rpData = await CDB.buildRPInstances(explanationResult, data, false, graknTx);
268+
data.nodes.push(...rpData.nodes);
269+
data.edges.push(...rpData.edges);
270+
271+
state.visFacade.addToCanvas(data);
272+
commit('updateCanvasData');
273+
const nodesWithAttributes = await computeAttributes(data.nodes, graknTx);
274+
275+
state.visFacade.updateNode(nodesWithAttributes);
276+
const styledEdges = data.edges.map(edge => ({ ...edge, label: edge.hiddenLabel, ...state.visStyle.computeExplanationEdgeStyle() }));
277+
state.visFacade.updateEdge(styledEdges);
278+
commit('loadingQuery', false);
279+
} else {
280+
commit('setGlobalErrorMsg', 'The transaction has been refreshed since the loading of this node and, as a result, the explaination is incomplete.');
281+
}
262282
}
263283
} catch (e) {
264284
await reopenTransaction(rootState, commit);

0 commit comments

Comments
 (0)