@_linked/rdf-mem-store 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +285 -0
- package/lib/cjs/Datafactory.d.ts +29 -0
- package/lib/cjs/Datafactory.js +101 -0
- package/lib/cjs/Datafactory.js.map +1 -0
- package/lib/cjs/InMemoryStore.d.ts +35 -0
- package/lib/cjs/InMemoryStore.js +98 -0
- package/lib/cjs/InMemoryStore.js.map +1 -0
- package/lib/cjs/collections/NodeMap.d.ts +35 -0
- package/lib/cjs/collections/NodeMap.js +244 -0
- package/lib/cjs/collections/NodeMap.js.map +1 -0
- package/lib/cjs/collections/NodeSet.d.ts +55 -0
- package/lib/cjs/collections/NodeSet.js +296 -0
- package/lib/cjs/collections/NodeSet.js.map +1 -0
- package/lib/cjs/collections/NodeURIMappings.d.ts +20 -0
- package/lib/cjs/collections/NodeURIMappings.js +65 -0
- package/lib/cjs/collections/NodeURIMappings.js.map +1 -0
- package/lib/cjs/collections/NodeValuesSet.d.ts +63 -0
- package/lib/cjs/collections/NodeValuesSet.js +100 -0
- package/lib/cjs/collections/NodeValuesSet.js.map +1 -0
- package/lib/cjs/collections/QuadArray.d.ts +17 -0
- package/lib/cjs/collections/QuadArray.js +67 -0
- package/lib/cjs/collections/QuadArray.js.map +1 -0
- package/lib/cjs/collections/QuadMap.d.ts +64 -0
- package/lib/cjs/collections/QuadMap.js +155 -0
- package/lib/cjs/collections/QuadMap.js.map +1 -0
- package/lib/cjs/collections/QuadSet.d.ts +22 -0
- package/lib/cjs/collections/QuadSet.js +106 -0
- package/lib/cjs/collections/QuadSet.js.map +1 -0
- package/lib/cjs/collections/SearchMap.d.ts +5 -0
- package/lib/cjs/collections/SearchMap.js +13 -0
- package/lib/cjs/collections/SearchMap.js.map +1 -0
- package/lib/cjs/events/EventBatcher.d.ts +20 -0
- package/lib/cjs/events/EventBatcher.js +97 -0
- package/lib/cjs/events/EventBatcher.js.map +1 -0
- package/lib/cjs/events/EventEmitter.d.ts +15 -0
- package/lib/cjs/events/EventEmitter.js +102 -0
- package/lib/cjs/events/EventEmitter.js.map +1 -0
- package/lib/cjs/index.d.ts +14 -0
- package/lib/cjs/index.js +48 -0
- package/lib/cjs/index.js.map +1 -0
- package/lib/cjs/interfaces/IGraphObject.d.ts +29 -0
- package/lib/cjs/interfaces/IGraphObject.js +3 -0
- package/lib/cjs/interfaces/IGraphObject.js.map +1 -0
- package/lib/cjs/interfaces/IGraphObjectSet.d.ts +24 -0
- package/lib/cjs/interfaces/IGraphObjectSet.js +3 -0
- package/lib/cjs/interfaces/IGraphObjectSet.js.map +1 -0
- package/lib/cjs/interfaces/IShape.d.ts +22 -0
- package/lib/cjs/interfaces/IShape.js +3 -0
- package/lib/cjs/interfaces/IShape.js.map +1 -0
- package/lib/cjs/interfaces/ISingleGraphObject.d.ts +3 -0
- package/lib/cjs/interfaces/ISingleGraphObject.js +3 -0
- package/lib/cjs/interfaces/ISingleGraphObject.js.map +1 -0
- package/lib/cjs/models.d.ts +1167 -0
- package/lib/cjs/models.js +2668 -0
- package/lib/cjs/models.js.map +1 -0
- package/lib/cjs/package.json +3 -0
- package/lib/cjs/utils/Debug.d.ts +3 -0
- package/lib/cjs/utils/Debug.js +46 -0
- package/lib/cjs/utils/Debug.js.map +1 -0
- package/lib/cjs/utils/LocalQueryResolver.d.ts +21 -0
- package/lib/cjs/utils/LocalQueryResolver.js +1442 -0
- package/lib/cjs/utils/LocalQueryResolver.js.map +1 -0
- package/lib/cjs/utils/URI.d.ts +18 -0
- package/lib/cjs/utils/URI.js +42 -0
- package/lib/cjs/utils/URI.js.map +1 -0
- package/lib/cjs/utils/toNamedNode.d.ts +8 -0
- package/lib/cjs/utils/toNamedNode.js +15 -0
- package/lib/cjs/utils/toNamedNode.js.map +1 -0
- package/lib/esm/Datafactory.d.ts +29 -0
- package/lib/esm/Datafactory.js +97 -0
- package/lib/esm/Datafactory.js.map +1 -0
- package/lib/esm/InMemoryStore.d.ts +35 -0
- package/lib/esm/InMemoryStore.js +94 -0
- package/lib/esm/InMemoryStore.js.map +1 -0
- package/lib/esm/collections/NodeMap.d.ts +35 -0
- package/lib/esm/collections/NodeMap.js +240 -0
- package/lib/esm/collections/NodeMap.js.map +1 -0
- package/lib/esm/collections/NodeSet.d.ts +55 -0
- package/lib/esm/collections/NodeSet.js +292 -0
- package/lib/esm/collections/NodeSet.js.map +1 -0
- package/lib/esm/collections/NodeURIMappings.d.ts +20 -0
- package/lib/esm/collections/NodeURIMappings.js +61 -0
- package/lib/esm/collections/NodeURIMappings.js.map +1 -0
- package/lib/esm/collections/NodeValuesSet.d.ts +63 -0
- package/lib/esm/collections/NodeValuesSet.js +96 -0
- package/lib/esm/collections/NodeValuesSet.js.map +1 -0
- package/lib/esm/collections/QuadArray.d.ts +17 -0
- package/lib/esm/collections/QuadArray.js +63 -0
- package/lib/esm/collections/QuadArray.js.map +1 -0
- package/lib/esm/collections/QuadMap.d.ts +64 -0
- package/lib/esm/collections/QuadMap.js +151 -0
- package/lib/esm/collections/QuadMap.js.map +1 -0
- package/lib/esm/collections/QuadSet.d.ts +22 -0
- package/lib/esm/collections/QuadSet.js +102 -0
- package/lib/esm/collections/QuadSet.js.map +1 -0
- package/lib/esm/collections/SearchMap.d.ts +5 -0
- package/lib/esm/collections/SearchMap.js +9 -0
- package/lib/esm/collections/SearchMap.js.map +1 -0
- package/lib/esm/events/EventBatcher.d.ts +20 -0
- package/lib/esm/events/EventBatcher.js +90 -0
- package/lib/esm/events/EventBatcher.js.map +1 -0
- package/lib/esm/events/EventEmitter.d.ts +15 -0
- package/lib/esm/events/EventEmitter.js +98 -0
- package/lib/esm/events/EventEmitter.js.map +1 -0
- package/lib/esm/index.d.ts +14 -0
- package/lib/esm/index.js +22 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/interfaces/IGraphObject.d.ts +29 -0
- package/lib/esm/interfaces/IGraphObject.js +2 -0
- package/lib/esm/interfaces/IGraphObject.js.map +1 -0
- package/lib/esm/interfaces/IGraphObjectSet.d.ts +24 -0
- package/lib/esm/interfaces/IGraphObjectSet.js +2 -0
- package/lib/esm/interfaces/IGraphObjectSet.js.map +1 -0
- package/lib/esm/interfaces/IShape.d.ts +22 -0
- package/lib/esm/interfaces/IShape.js +2 -0
- package/lib/esm/interfaces/IShape.js.map +1 -0
- package/lib/esm/interfaces/ISingleGraphObject.d.ts +3 -0
- package/lib/esm/interfaces/ISingleGraphObject.js +2 -0
- package/lib/esm/interfaces/ISingleGraphObject.js.map +1 -0
- package/lib/esm/models.d.ts +1167 -0
- package/lib/esm/models.js +2659 -0
- package/lib/esm/models.js.map +1 -0
- package/lib/esm/package.json +3 -0
- package/lib/esm/utils/Debug.d.ts +3 -0
- package/lib/esm/utils/Debug.js +42 -0
- package/lib/esm/utils/Debug.js.map +1 -0
- package/lib/esm/utils/LocalQueryResolver.d.ts +21 -0
- package/lib/esm/utils/LocalQueryResolver.js +1434 -0
- package/lib/esm/utils/LocalQueryResolver.js.map +1 -0
- package/lib/esm/utils/URI.d.ts +18 -0
- package/lib/esm/utils/URI.js +38 -0
- package/lib/esm/utils/URI.js.map +1 -0
- package/lib/esm/utils/toNamedNode.d.ts +8 -0
- package/lib/esm/utils/toNamedNode.js +12 -0
- package/lib/esm/utils/toNamedNode.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1,1442 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createLocal = createLocal;
|
|
13
|
+
exports.deleteLocal = deleteLocal;
|
|
14
|
+
exports.updateLocal = updateLocal;
|
|
15
|
+
exports.resolveLocal = resolveLocal;
|
|
16
|
+
exports.resolveLocalEndResults = resolveLocalEndResults;
|
|
17
|
+
exports.resolveQueryPropertyPath = resolveQueryPropertyPath;
|
|
18
|
+
const SelectQuery_1 = require("@_linked/core/queries/SelectQuery");
|
|
19
|
+
const ShapeSet_1 = require("@_linked/core/collections/ShapeSet");
|
|
20
|
+
const Shape_1 = require("@_linked/core/shapes/Shape");
|
|
21
|
+
const shacl_1 = require("@_linked/core/ontologies/shacl");
|
|
22
|
+
const CoreMap_1 = require("@_linked/core/collections/CoreMap");
|
|
23
|
+
const QueryFactory_1 = require("@_linked/core/queries/QueryFactory");
|
|
24
|
+
const models_js_1 = require("../models.js");
|
|
25
|
+
const xsd_1 = require("@_linked/core/ontologies/xsd");
|
|
26
|
+
const rdf_1 = require("@_linked/core/ontologies/rdf");
|
|
27
|
+
const NodeSet_js_1 = require("../collections/NodeSet.js");
|
|
28
|
+
const toNamedNode_js_1 = require("./toNamedNode.js");
|
|
29
|
+
const ShapeClass_1 = require("@_linked/core/utils/ShapeClass");
|
|
30
|
+
const primitiveTypes = ['string', 'number', 'boolean', 'Date'];
|
|
31
|
+
/**
|
|
32
|
+
* Convert a property path from core's NodeReferenceValue format to NamedNode(s).
|
|
33
|
+
*/
|
|
34
|
+
function toPropertyPath(path) {
|
|
35
|
+
if (Array.isArray(path)) {
|
|
36
|
+
return path.map(p => (0, toNamedNode_js_1.toNamedNode)(p));
|
|
37
|
+
}
|
|
38
|
+
return (0, toNamedNode_js_1.toNamedNode)(path);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Find all instances of a type (and its subtypes) in the global graph.
|
|
42
|
+
* Replaces Shape.getLocalInstancesByType() which doesn't exist in core.
|
|
43
|
+
*/
|
|
44
|
+
function getInstancesByType(shapeClass) {
|
|
45
|
+
if (!shapeClass.targetClass) {
|
|
46
|
+
return new NodeSet_js_1.NodeSet();
|
|
47
|
+
}
|
|
48
|
+
const typeNode = (0, toNamedNode_js_1.toNamedNode)(shapeClass.targetClass);
|
|
49
|
+
const rdfType = (0, toNamedNode_js_1.toNamedNode)(rdf_1.rdf.type);
|
|
50
|
+
let nodes = typeNode.getAllInverse(rdfType) || new NodeSet_js_1.NodeSet();
|
|
51
|
+
// Also get instances of subtypes
|
|
52
|
+
try {
|
|
53
|
+
(0, ShapeClass_1.getSubShapesClasses)(shapeClass).forEach((sub) => {
|
|
54
|
+
if (sub.targetClass) {
|
|
55
|
+
const subNodes = (0, toNamedNode_js_1.toNamedNode)(sub.targetClass).getAllInverse(rdfType);
|
|
56
|
+
if (subNodes) {
|
|
57
|
+
subNodes.forEach((n) => nodes.add(n));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// getSubShapesClasses may not work for all shape types
|
|
64
|
+
}
|
|
65
|
+
return nodes;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Convert a ShapeSet to a NodeSet by looking up each shape's id as a NamedNode.
|
|
69
|
+
* Replaces ShapeSet.getNodes() which doesn't exist in core.
|
|
70
|
+
*/
|
|
71
|
+
function shapeSetToNodeSet(set) {
|
|
72
|
+
const nodes = new NodeSet_js_1.NodeSet();
|
|
73
|
+
set.forEach((s) => nodes.add(models_js_1.NamedNode.getOrCreate(s.id)));
|
|
74
|
+
return nodes;
|
|
75
|
+
}
|
|
76
|
+
function createLocal(query) {
|
|
77
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
78
|
+
if (query.type === 'create') {
|
|
79
|
+
//convert the description of the node to create just like in update(),
|
|
80
|
+
// but this time there is no parent propertyShape, so we use null
|
|
81
|
+
//this will also set the rdf:type and save() the node.
|
|
82
|
+
const { value, plainValue } = yield convertNodeDescription(null, query.description, true);
|
|
83
|
+
return plainValue;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw new Error('Unknown query type: ' + query.type);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function deleteLocal(query) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
if (query.type === 'delete') {
|
|
93
|
+
const response = {
|
|
94
|
+
deleted: [],
|
|
95
|
+
count: 0,
|
|
96
|
+
};
|
|
97
|
+
const errors = {};
|
|
98
|
+
const failed = [];
|
|
99
|
+
query.ids.forEach((id) => {
|
|
100
|
+
let subject;
|
|
101
|
+
try {
|
|
102
|
+
subject = convertNodeReferenceOrId(null, id);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
let idString = typeof id === 'string'
|
|
106
|
+
? id
|
|
107
|
+
: (id === null || id === void 0 ? void 0 : id.id)
|
|
108
|
+
? id.id
|
|
109
|
+
: id && id['uri']
|
|
110
|
+
? id['uri']
|
|
111
|
+
: '';
|
|
112
|
+
if (idString === '') {
|
|
113
|
+
errors[Object.keys(errors).length] = 'Invalid id: ' + id;
|
|
114
|
+
failed.push(id);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
errors[idString] = 'Could not find node with id: ' + idString;
|
|
118
|
+
failed.push(idString);
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!subject.value) {
|
|
123
|
+
errors[subject.plainValue.id] =
|
|
124
|
+
'No node found with id: ' + subject.plainValue.id;
|
|
125
|
+
failed.push(subject.plainValue.id);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
//remove the node from the graph
|
|
129
|
+
subject.value.remove();
|
|
130
|
+
response.deleted.push(subject.plainValue.id);
|
|
131
|
+
response.count++;
|
|
132
|
+
});
|
|
133
|
+
if (failed.length > 0) {
|
|
134
|
+
response.failed = failed;
|
|
135
|
+
response.errors = errors;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
throw new Error('Invalid query type: ' + query.type);
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
function updateLocal(query) {
|
|
145
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
if (query.type === 'update') {
|
|
147
|
+
let subject = models_js_1.NamedNode.getNamedNode(query.id);
|
|
148
|
+
if (!subject) {
|
|
149
|
+
throw new Error('No subject found for id: ' + query.id);
|
|
150
|
+
}
|
|
151
|
+
// let shapeClass = getShapeClass(query.shape.namedNode);
|
|
152
|
+
// let shape = new (shapeClass as any)(subject);
|
|
153
|
+
let plainResults = yield applyFieldUpdates(query.updates.fields, subject);
|
|
154
|
+
plainResults['id'] = query.id;
|
|
155
|
+
return plainResults;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
throw new Error('Invalid query type: ' + query.type);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
function applyFieldUpdates(fields_1, subject_1) {
|
|
163
|
+
return __awaiter(this, arguments, void 0, function* (fields, subject, createQuery = false) {
|
|
164
|
+
let plainValues = {};
|
|
165
|
+
for (let field of fields) {
|
|
166
|
+
let propShape = field.prop;
|
|
167
|
+
let propertyPath = toPropertyPath(propShape.path);
|
|
168
|
+
if (typeof field.val === 'undefined') {
|
|
169
|
+
unsetPropertyPath(subject, propertyPath);
|
|
170
|
+
if (propShape.maxCount >= 1) {
|
|
171
|
+
//when clearing a single property we return undefined
|
|
172
|
+
plainValues[propShape.label] = undefined;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
plainValues[propShape.label] = [];
|
|
176
|
+
//when clearing a set of values we return an empty array
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else if (Array.isArray(field.val)) {
|
|
180
|
+
(0, QueryFactory_1.checkNewCount)(propShape, field.val.length);
|
|
181
|
+
let values = [];
|
|
182
|
+
let plainValueArr = [];
|
|
183
|
+
//see check above, we already know it's an array, so we can cast it
|
|
184
|
+
for (let singleVal of field.val) {
|
|
185
|
+
let res = yield convertValue(propShape, singleVal, createQuery);
|
|
186
|
+
plainValueArr.push(res.plainValue);
|
|
187
|
+
values.push(res.value);
|
|
188
|
+
}
|
|
189
|
+
if (values.every((v) => typeof v === 'undefined')) {
|
|
190
|
+
//clearing a property
|
|
191
|
+
plainValues[propShape.label] = undefined;
|
|
192
|
+
unsetPropertyPath(subject, propertyPath);
|
|
193
|
+
}
|
|
194
|
+
else if (values.some((v) => typeof v === 'undefined')) {
|
|
195
|
+
throw new Error('Invalid use of undefined for property: ' +
|
|
196
|
+
propShape.label +
|
|
197
|
+
'. You cannot mix undefined with defined values. Values given:' +
|
|
198
|
+
values.map((v) => v === null || v === void 0 ? void 0 : v.toString()).join(', '));
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// For multi-value properties, return updatedTo structure if this is an UPDATE query (if it's a CREATE query we just return the array)
|
|
202
|
+
plainValues[propShape.label] = createQuery
|
|
203
|
+
? plainValueArr
|
|
204
|
+
: { updatedTo: plainValueArr };
|
|
205
|
+
overwritePropertyPathMultipleValues(subject, propertyPath, values);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else if ((0, QueryFactory_1.isSetModificationValue)(field.val)) {
|
|
209
|
+
//check if the new UPDATED number of properties would be allowed
|
|
210
|
+
//by getting the current values, and counting how many remain after adding/removing values
|
|
211
|
+
const currentValues = getPropertyPath(subject, propertyPath);
|
|
212
|
+
const numCurrentValues = currentValues.size;
|
|
213
|
+
const numFinalValues = numCurrentValues +
|
|
214
|
+
(field.val.$add ? field.val.$add.length : 0) -
|
|
215
|
+
(field.val.$remove ? field.val.$remove.length : 0);
|
|
216
|
+
(0, QueryFactory_1.checkNewCount)(propShape, numFinalValues);
|
|
217
|
+
//prepare object to keep track of the plain values that are added and removed
|
|
218
|
+
const plainUpdates = {};
|
|
219
|
+
if (field.val.$remove) {
|
|
220
|
+
let removedPlainValues = [];
|
|
221
|
+
//remove the values from the property path
|
|
222
|
+
field.val.$remove.forEach((val) => {
|
|
223
|
+
//convert the node reference value to a real node
|
|
224
|
+
let nodeToRemove = convertNodeReference(propShape, val, '$remove');
|
|
225
|
+
//keep track of what's removed
|
|
226
|
+
removedPlainValues.push(nodeToRemove.plainValue);
|
|
227
|
+
//remove the value from the property path
|
|
228
|
+
unsetPropertyPathValue(subject, propertyPath, nodeToRemove.value);
|
|
229
|
+
});
|
|
230
|
+
plainUpdates.removed = removedPlainValues;
|
|
231
|
+
}
|
|
232
|
+
if (field.val.$add) {
|
|
233
|
+
let addedPlainValues = [];
|
|
234
|
+
//add the values to the property path
|
|
235
|
+
let values = [];
|
|
236
|
+
for (let singleVal of field.val.$add) {
|
|
237
|
+
//convert the value (which can be a node reference or a node description)
|
|
238
|
+
let res = yield convertValue(propShape, singleVal, createQuery);
|
|
239
|
+
//keep track of what's added
|
|
240
|
+
addedPlainValues.push(res.plainValue);
|
|
241
|
+
values.push(res.value);
|
|
242
|
+
}
|
|
243
|
+
//add the new values to the set of values at the end of the path
|
|
244
|
+
addToResultSets(subject, propertyPath, values);
|
|
245
|
+
//if all that went well, keep track of the added values
|
|
246
|
+
plainUpdates.added = addedPlainValues;
|
|
247
|
+
}
|
|
248
|
+
plainValues[propShape.label] = plainUpdates;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
//single value is provided
|
|
252
|
+
//check if that fits with the maxCount and minCount of the property
|
|
253
|
+
(0, QueryFactory_1.checkNewCount)(propShape, 1);
|
|
254
|
+
let res = yield convertValue(propShape, field.val, createQuery);
|
|
255
|
+
// if(typeof res.value === 'undefined') {
|
|
256
|
+
// unsetPropertyPath(subject,propertyPath);
|
|
257
|
+
// plainValues[propShape.label] = undefined;
|
|
258
|
+
// } else {
|
|
259
|
+
//save the plain value for the result
|
|
260
|
+
plainValues[propShape.label] = res.plainValue;
|
|
261
|
+
//Note, we are using SET here, to ADD a value.
|
|
262
|
+
//If there are multiple values possible and the user wants to overwrite all the values,
|
|
263
|
+
//they need to use an update function instead of an update object
|
|
264
|
+
overwritePropertyPathSingleValue(subject, propertyPath, res.value);
|
|
265
|
+
// }
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return plainValues;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function getPropertyPath(subject, path) {
|
|
272
|
+
if (Array.isArray(path)) {
|
|
273
|
+
let target = new NodeSet_js_1.NodeSet([subject]);
|
|
274
|
+
for (let p of path) {
|
|
275
|
+
target = target.getAll(p);
|
|
276
|
+
}
|
|
277
|
+
return target;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
return subject.getAll(path);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function addToResultSets(subject, path, values) {
|
|
284
|
+
if (Array.isArray(path)) {
|
|
285
|
+
//save the last property, that's the one we want to add values to
|
|
286
|
+
let lastPath = path.pop();
|
|
287
|
+
let target = new NodeSet_js_1.NodeSet([subject]);
|
|
288
|
+
//for the remaining parts, follow the path to the end
|
|
289
|
+
for (let p of path) {
|
|
290
|
+
target = target.getAll(p);
|
|
291
|
+
}
|
|
292
|
+
//for each node in the target nodes, add the values with the last property from the path as predicate
|
|
293
|
+
//the existing quads with this subject and predicate will remain, and the new values will be added to the graph
|
|
294
|
+
target.msetEach(lastPath, values);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
//if it's a single property, we can just add the values with the given path as predicate
|
|
298
|
+
//the existing quads with this subject and predicate will remain, and the new values will be added to the graph
|
|
299
|
+
subject.mset(path, values);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function overwritePropertyPathMultipleValues(subject, path, values) {
|
|
303
|
+
if (Array.isArray(path)) {
|
|
304
|
+
//NOTE: for now we are removing the entire path, not just the last part of the path
|
|
305
|
+
// Not sure yet if we need to distinguish between the two
|
|
306
|
+
console.warn(`Overwriting each end values in property path (${path.map((p) => p.uri).join(' -> ')}) with multiple values ${values.map((v) => v.uri).join(', ')}. Is that expected behaviour?`);
|
|
307
|
+
let lastPath = path.pop();
|
|
308
|
+
let target = new NodeSet_js_1.NodeSet([subject]);
|
|
309
|
+
for (let p of path) {
|
|
310
|
+
target = target.getAll(p);
|
|
311
|
+
}
|
|
312
|
+
target.forEach((node) => {
|
|
313
|
+
node.moverwrite(lastPath, values);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
subject.moverwrite(path, values);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function overwritePropertyPathSingleValue(subject, path, value) {
|
|
321
|
+
if (Array.isArray(path)) {
|
|
322
|
+
//NOTE: for now we are removing the entire path, not just the last part of the path
|
|
323
|
+
// Not sure yet if we need to distinguish between the two
|
|
324
|
+
console.warn(`Overwriting each end values in property path (${path.map((p) => p.uri).join(' -> ')}) with single value ${value.toString()}. Is that expected behaviour? `);
|
|
325
|
+
let lastPath = path.pop();
|
|
326
|
+
let target = subject;
|
|
327
|
+
for (let p of path) {
|
|
328
|
+
target = target.getAll(p);
|
|
329
|
+
}
|
|
330
|
+
target.forEach((node) => {
|
|
331
|
+
node.overwrite(lastPath, value);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
subject.overwrite(path, value);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function unsetPropertyPathValue(subject, path, value) {
|
|
339
|
+
if (Array.isArray(path)) {
|
|
340
|
+
//NOTE: for unsetting a specific value we are just unsetting the final connection NOT the entire path
|
|
341
|
+
console.warn(`Unsetting each end value in property path (${path.map((p) => p.uri).join(' -> ')}) with value ${value.toString()}. Is that expected behaviour? `);
|
|
342
|
+
let lastPath = path.pop();
|
|
343
|
+
let target = new NodeSet_js_1.NodeSet([subject]);
|
|
344
|
+
for (let p of path) {
|
|
345
|
+
target = target.getAll(p);
|
|
346
|
+
}
|
|
347
|
+
target.forEach((node) => {
|
|
348
|
+
node.unset(lastPath, value);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
subject.unset(path, value);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function unsetPropertyPath(subject, path) {
|
|
356
|
+
if (Array.isArray(path)) {
|
|
357
|
+
//NOTE: for now we are removing the last part of the path, disconnecting the end values from the subject at the final property of the path
|
|
358
|
+
// If we need to remove the entire path this should likely be done with other structures, like a ItemListElement being dependent on having an item defined and automatically being removed when we remove the item
|
|
359
|
+
console.warn('Unsetting the final property-value pair of the property path. Is that expected behaviour? : ' +
|
|
360
|
+
path.map((p) => p.uri).join(' -> '));
|
|
361
|
+
let lastPath = path.pop();
|
|
362
|
+
let targets = new NodeSet_js_1.NodeSet([subject]);
|
|
363
|
+
for (let p of path) {
|
|
364
|
+
targets = targets.getAll(p);
|
|
365
|
+
}
|
|
366
|
+
targets.forEach((node) => {
|
|
367
|
+
node.unsetAll(lastPath);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
subject.unsetAll(path);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function convertValue(propShape_1, value_1) {
|
|
375
|
+
return __awaiter(this, arguments, void 0, function* (propShape, value, createQuery = false) {
|
|
376
|
+
var _a;
|
|
377
|
+
const nkId = (_a = propShape.nodeKind) === null || _a === void 0 ? void 0 : _a.id;
|
|
378
|
+
if (nkId === shacl_1.shacl.Literal.id) {
|
|
379
|
+
return convertLiteral(propShape, value);
|
|
380
|
+
}
|
|
381
|
+
else if (nkId === shacl_1.shacl.BlankNodeOrIRI.id ||
|
|
382
|
+
nkId === shacl_1.shacl.BlankNode.id ||
|
|
383
|
+
nkId === shacl_1.shacl.IRI.id) {
|
|
384
|
+
return yield convertNamedNode(propShape, value, createQuery);
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
//we currently don't support other node kinds, like shacl.BlankNodeOrLiteral and shacl.BlankNodeOrIRI
|
|
388
|
+
//so in this case, we allow all types of values,
|
|
389
|
+
//next we look at datatype and shapeValue to determine the correct type of value
|
|
390
|
+
if (propShape.datatype) {
|
|
391
|
+
return convertLiteral(propShape, value);
|
|
392
|
+
}
|
|
393
|
+
else if (propShape.valueShape) {
|
|
394
|
+
return yield convertNamedNode(propShape, value, createQuery);
|
|
395
|
+
}
|
|
396
|
+
//these are clearly meant to be literals
|
|
397
|
+
if (typeof value === 'number' ||
|
|
398
|
+
typeof value === 'boolean' ||
|
|
399
|
+
value instanceof Date ||
|
|
400
|
+
typeof value === 'string') {
|
|
401
|
+
return convertLiteral(propShape, value);
|
|
402
|
+
}
|
|
403
|
+
//arrays mean it's an array of field+value objects
|
|
404
|
+
else if (Array.isArray(value)) {
|
|
405
|
+
return yield convertNamedNode(propShape, value, createQuery);
|
|
406
|
+
}
|
|
407
|
+
throw new Error('Unknown value type for property: ' + propShape.label);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
function convertNamedNode(propShape, value, createQuery = true) {
|
|
412
|
+
//value is expected to be an array of fields, or an object with an id for a direct node reference
|
|
413
|
+
if (isNodeReference(value)) {
|
|
414
|
+
return Promise.resolve(convertNodeReference(propShape, value));
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
return convertNodeDescription(propShape, value, createQuery);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function isNodeReference(value) {
|
|
421
|
+
//check if the value is an object with an id field
|
|
422
|
+
//NOTE: all objects with an id key are considered node references
|
|
423
|
+
//and all other properties are ignored
|
|
424
|
+
//to DEFINE the ID of a new node, the user should use __id as a key in the object
|
|
425
|
+
return typeof value === 'object' && value !== null && 'id' in value;
|
|
426
|
+
// && Object.keys(value).length === 1;
|
|
427
|
+
//and check if there is only 1 key in the object
|
|
428
|
+
}
|
|
429
|
+
function convertNodeReferenceOrId(propShape, value, suffixKey) {
|
|
430
|
+
if (typeof value === 'string') {
|
|
431
|
+
return {
|
|
432
|
+
value: models_js_1.NamedNode.getNamedNode(value),
|
|
433
|
+
plainValue: { id: value },
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
return convertNodeReference(propShape, value, suffixKey);
|
|
437
|
+
}
|
|
438
|
+
function convertNodeReference(propShape, value, suffixKey) {
|
|
439
|
+
if (!value.id) {
|
|
440
|
+
throw new Error('Expected a node reference for property: ' +
|
|
441
|
+
(propShape === null || propShape === void 0 ? void 0 : propShape.label) +
|
|
442
|
+
(suffixKey ? '.' + suffixKey : ''));
|
|
443
|
+
}
|
|
444
|
+
//if other keys are present
|
|
445
|
+
if (Object.keys(value).length > 1) {
|
|
446
|
+
throw new Error('Invalid value for property: ' +
|
|
447
|
+
propShape.label +
|
|
448
|
+
(suffixKey ? '.' + suffixKey : '') +
|
|
449
|
+
'. A node reference should only contain the id field.');
|
|
450
|
+
}
|
|
451
|
+
//NOTE: changed this to getOrCreate. Which means also unknown id's will be converted to a named node
|
|
452
|
+
//We need this for example when we load shapes in one app of another app, and the shapes are not yet defined in the graph
|
|
453
|
+
return {
|
|
454
|
+
value: models_js_1.NamedNode.getOrCreate(value.id),
|
|
455
|
+
//return an object only with the ID (a NodeReferenceValue should always only have an id field)
|
|
456
|
+
plainValue: { id: value.id },
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
function convertNodeDescription(propShape_1, value_1) {
|
|
460
|
+
return __awaiter(this, arguments, void 0, function* (propShape, value, createQuery = false) {
|
|
461
|
+
if (!value.shape || !value.fields) {
|
|
462
|
+
throw new Error('Expected a node description for property: ' + (propShape === null || propShape === void 0 ? void 0 : propShape.label));
|
|
463
|
+
}
|
|
464
|
+
//use the provided id as URI or create a new node if not defined
|
|
465
|
+
let node = value.__id
|
|
466
|
+
? models_js_1.NamedNode.getOrCreate(value.__id)
|
|
467
|
+
: models_js_1.NamedNode.create();
|
|
468
|
+
let plainResults = yield applyFieldUpdates(value.fields, node, createQuery);
|
|
469
|
+
let valueShape = (propShape === null || propShape === void 0 ? void 0 : propShape.valueShape) || value.shape;
|
|
470
|
+
//if this property comes with a restriction that all values need to be of a certain shape
|
|
471
|
+
if (valueShape && 'targetClass' in valueShape && valueShape.targetClass) {
|
|
472
|
+
//then we set the type of the node to the target class
|
|
473
|
+
//this is a "free" automatic property that we set for the user, so they don't need to always manually type it into the create() or update() queries
|
|
474
|
+
node.set((0, toNamedNode_js_1.toNamedNode)(rdf_1.rdf.type), (0, toNamedNode_js_1.toNamedNode)(valueShape.targetClass));
|
|
475
|
+
}
|
|
476
|
+
//mark as non-temporary so save() is a no-op in local-only context
|
|
477
|
+
//data is already in the global graph from set()/overwrite() calls above
|
|
478
|
+
node.isTemporaryNode = false;
|
|
479
|
+
plainResults['id'] = node.uri;
|
|
480
|
+
return {
|
|
481
|
+
value: node,
|
|
482
|
+
plainValue: plainResults,
|
|
483
|
+
};
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
function convertLiteral(propShape, value) {
|
|
487
|
+
var _a;
|
|
488
|
+
if (typeof value === 'object' && !(value instanceof Date)) {
|
|
489
|
+
throw new Error('Object values are not allowed for property: ' + propShape.label);
|
|
490
|
+
}
|
|
491
|
+
let datatype = propShape.datatype;
|
|
492
|
+
let res;
|
|
493
|
+
if (datatype) {
|
|
494
|
+
const dtId = (_a = datatype.id) !== null && _a !== void 0 ? _a : datatype;
|
|
495
|
+
if (dtId === xsd_1.xsd.integer.id) {
|
|
496
|
+
if (typeof value === 'number') {
|
|
497
|
+
res = new models_js_1.Literal(value.toString(), (0, toNamedNode_js_1.toNamedNode)(xsd_1.xsd.integer));
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
throw new Error(`Property ${propShape.parentNodeShape.label}.${propShape.label} has datatype xsd.integer, so it expects a number value. Given value: ` +
|
|
501
|
+
JSON.stringify(value) +
|
|
502
|
+
' of type: ' +
|
|
503
|
+
typeof value);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else if (dtId === xsd_1.xsd.boolean.id) {
|
|
507
|
+
if (typeof value === 'boolean') {
|
|
508
|
+
res = Boolean_toLiteral(value);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
throw new Error(`Property ${propShape.parentNodeShape.label}.${propShape.label} has datatype xsd.boolean, so it expects a boolean value. Given value: ` +
|
|
512
|
+
JSON.stringify(value) +
|
|
513
|
+
' of type: ' +
|
|
514
|
+
typeof value);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
else if (dtId === xsd_1.xsd.string.id) {
|
|
518
|
+
res = new models_js_1.Literal(value.toString(), (0, toNamedNode_js_1.toNamedNode)(xsd_1.xsd.string));
|
|
519
|
+
}
|
|
520
|
+
else if (dtId === xsd_1.xsd.date.id || dtId === xsd_1.xsd.dateTime.id) {
|
|
521
|
+
//check if value is a date
|
|
522
|
+
if (value instanceof Date) {
|
|
523
|
+
res = XSDDate_fromNativeDate(value, datatype);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
throw new Error(`Property ${propShape.parentNodeShape.label}.${propShape.label} has datatype xsd.dateTime, so it expects a Date value. Given value: ` +
|
|
527
|
+
JSON.stringify(value) +
|
|
528
|
+
' of type: ' +
|
|
529
|
+
typeof value);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
console.warn(`Unknown datatype :${datatype.toString()}. Assuming it's a string value`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (typeof value === 'undefined') {
|
|
537
|
+
return {
|
|
538
|
+
value: undefined,
|
|
539
|
+
plainValue: undefined,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (value === null) {
|
|
543
|
+
throw new Error('Value cannot be null. If you want to unset a value, use undefined');
|
|
544
|
+
}
|
|
545
|
+
//if none of the previous options matched (and therefor res is not set yet), then we assume the value is a string
|
|
546
|
+
if (!res) {
|
|
547
|
+
if (typeof value !== 'string') {
|
|
548
|
+
throw new Error(`Property ${propShape.parentNodeShape.label}.${propShape.label} has no datatype defined in its decorator, so it expects a string value. Given value: ` +
|
|
549
|
+
JSON.stringify(value) +
|
|
550
|
+
' of type: ' +
|
|
551
|
+
typeof value);
|
|
552
|
+
}
|
|
553
|
+
//and we convert the string to a literal
|
|
554
|
+
//Note: datatype could be null or any other unsupported datatype
|
|
555
|
+
res = new models_js_1.Literal(value, datatype ? (0, toNamedNode_js_1.toNamedNode)(datatype) : null);
|
|
556
|
+
}
|
|
557
|
+
return {
|
|
558
|
+
value: res,
|
|
559
|
+
plainValue: value,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Resolves the query locally, by searching the graph in local memory, without using stores.
|
|
564
|
+
* Returns the result immediately.
|
|
565
|
+
* The results will be the end point reached by the query
|
|
566
|
+
*/
|
|
567
|
+
function resolveLocal(query) {
|
|
568
|
+
//TODO: review if we need the shape here or if we can get it from the query
|
|
569
|
+
// if(!shape) {
|
|
570
|
+
// shape = query.subject
|
|
571
|
+
// }
|
|
572
|
+
let subject;
|
|
573
|
+
if (query.subject) {
|
|
574
|
+
if ('id' in query.subject) {
|
|
575
|
+
if (typeof query.subject.id !== 'string') {
|
|
576
|
+
throw new Error('When providing a subject in a query, the id must be a string. Given: ' +
|
|
577
|
+
JSON.stringify(query.subject.id));
|
|
578
|
+
}
|
|
579
|
+
if (models_js_1.NamedNode.getNamedNode(query.subject.id)) {
|
|
580
|
+
// subject = query.shape.getFromURI((query.subject as QResult<any>).id) as Shape;
|
|
581
|
+
subject = models_js_1.NamedNode.getOrCreate(query.subject.id);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else if (query.subject instanceof ShapeSet_1.ShapeSet) {
|
|
588
|
+
subject = shapeSetToNodeSet(query.subject);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
subject = models_js_1.NamedNode.getOrCreate(query.subject.id);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
subject = getInstancesByType(query.shape);
|
|
596
|
+
}
|
|
597
|
+
// let subject2 = query.subject ? query.subject : query.shape.getLocalInstancesByType();
|
|
598
|
+
// console.log(ValidationReport.printForShapeInstances(query.shape));
|
|
599
|
+
//filter the instances down based on the where clause
|
|
600
|
+
if (query.where) {
|
|
601
|
+
subject = filterResults(subject, query.where);
|
|
602
|
+
}
|
|
603
|
+
//sort the instances before slicing
|
|
604
|
+
if (query.sortBy) {
|
|
605
|
+
subject = sortResults(subject, query.sortBy);
|
|
606
|
+
}
|
|
607
|
+
//slice the instances based on the limit and offset
|
|
608
|
+
if (query.limit && subject instanceof NodeSet_js_1.NodeSet) {
|
|
609
|
+
subject = subject.slice(query.offset || 0, (query.offset || 0) + query.limit);
|
|
610
|
+
}
|
|
611
|
+
let resultObjects;
|
|
612
|
+
if (query.subject instanceof ShapeSet_1.ShapeSet) {
|
|
613
|
+
resultObjects = nodesToResultObjects(subject);
|
|
614
|
+
}
|
|
615
|
+
else if (query.subject instanceof Shape_1.Shape) {
|
|
616
|
+
resultObjects = shapeToResultObject(subject);
|
|
617
|
+
}
|
|
618
|
+
else if (query.subject && query.subject.id) {
|
|
619
|
+
//when a query subject is given as an object with an id, probably from a previous query result
|
|
620
|
+
resultObjects = {
|
|
621
|
+
id: query.subject.id,
|
|
622
|
+
// shape: query.shape,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
//no specific subject is given, so subjects will be a NodeSet of filtered instances,
|
|
627
|
+
resultObjects = nodesToResultObjects(subject);
|
|
628
|
+
}
|
|
629
|
+
//SELECT - go over the select path and resolve the values
|
|
630
|
+
if (Array.isArray(query.select)) {
|
|
631
|
+
query.select.forEach((queryPath) => {
|
|
632
|
+
resolveQueryPath(subject, queryPath, resultObjects);
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
const r = (singleShape) => resolveCustomObject(singleShape, query.select, resultObjects instanceof Map
|
|
637
|
+
? resultObjects.get(singleShape.uri)
|
|
638
|
+
: resultObjects);
|
|
639
|
+
query.subject ? r(subject) : subject.map(r);
|
|
640
|
+
}
|
|
641
|
+
const results = (resultObjects instanceof Map ? [...resultObjects.values()] : resultObjects);
|
|
642
|
+
if (query.singleResult) {
|
|
643
|
+
return Array.isArray(results) ? results[0] : results;
|
|
644
|
+
}
|
|
645
|
+
return results;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* resolves each key of the custom query object
|
|
649
|
+
* and writes the result to the resultObject with the same keys
|
|
650
|
+
* @param subject
|
|
651
|
+
* @param query
|
|
652
|
+
* @param resultObject
|
|
653
|
+
*/
|
|
654
|
+
function resolveCustomObject(subject, query, resultObject) {
|
|
655
|
+
for (let key of Object.getOwnPropertyNames(query)) {
|
|
656
|
+
let result = resolveQueryPath(subject, query[key]);
|
|
657
|
+
writeResultObject(resultObject, key, result);
|
|
658
|
+
}
|
|
659
|
+
return resultObject;
|
|
660
|
+
}
|
|
661
|
+
function writeResultObject(resultObject, key, result) {
|
|
662
|
+
//convert undefined to null, because JSON.stringify will KEEP keys that have a null value. Which is required for LINCD to work properly with nested queries
|
|
663
|
+
if (typeof result === 'undefined') {
|
|
664
|
+
result = null;
|
|
665
|
+
}
|
|
666
|
+
//if this key was already set
|
|
667
|
+
if (key in resultObject) {
|
|
668
|
+
//if both the existing value and the new value are objects, we can merge them
|
|
669
|
+
if (result &&
|
|
670
|
+
resultObject[key] &&
|
|
671
|
+
typeof result === 'object' &&
|
|
672
|
+
typeof resultObject[key] === 'object') {
|
|
673
|
+
resultObject[key] = Object.assign(Object.assign({}, resultObject[key]), result);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
else if (result && result[key] !== null) {
|
|
677
|
+
console.warn('Overwriting existing value for key: ' +
|
|
678
|
+
key +
|
|
679
|
+
' in result object. Existing value: ' +
|
|
680
|
+
JSON.stringify(resultObject[key]) +
|
|
681
|
+
', new value: ' +
|
|
682
|
+
JSON.stringify(result));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
resultObject[key] = result;
|
|
686
|
+
}
|
|
687
|
+
function resolveLocalEndResults(query, subject, queryPaths) {
|
|
688
|
+
queryPaths = queryPaths || query.getQueryPaths();
|
|
689
|
+
subject = subject || query.shape.getLocalInstances();
|
|
690
|
+
let results = [];
|
|
691
|
+
if (Array.isArray(queryPaths)) {
|
|
692
|
+
queryPaths.forEach((queryPath) => {
|
|
693
|
+
results.push(resolveQueryPathEndResults(subject, queryPath));
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
else {
|
|
697
|
+
throw new Error('TODO: implement support for custom query object: ' + queryPaths);
|
|
698
|
+
}
|
|
699
|
+
// convert the result of each instance into the shape that was requested
|
|
700
|
+
if (query.traceResponse instanceof SelectQuery_1.QueryBuilderObject) {
|
|
701
|
+
//even though resolveQueryPaths always returns an array, if a single value was requested
|
|
702
|
+
//we will return the first value of that array to match the request
|
|
703
|
+
return results.shift();
|
|
704
|
+
//map((result) => {
|
|
705
|
+
//return result.shift();
|
|
706
|
+
//});
|
|
707
|
+
}
|
|
708
|
+
else if (Array.isArray(query.traceResponse)) {
|
|
709
|
+
//nothing to convert if an array was requested
|
|
710
|
+
return results;
|
|
711
|
+
}
|
|
712
|
+
else if (
|
|
713
|
+
// query.traceResponse instanceof QueryValueSetOfSets ||
|
|
714
|
+
query.traceResponse instanceof SelectQuery_1.SelectQueryFactory) {
|
|
715
|
+
return results.shift();
|
|
716
|
+
}
|
|
717
|
+
else if (query.traceResponse instanceof SelectQuery_1.QueryPrimitiveSet ||
|
|
718
|
+
query.traceResponse instanceof SelectQuery_1.Evaluation) {
|
|
719
|
+
//TODO: see how traceResponse is made for QueryValue. Here we need to return an array of the first item in the results?
|
|
720
|
+
//does that also work if there is multiple values?
|
|
721
|
+
//do we need to check the size of the traceresponse
|
|
722
|
+
//why is a CoreSet created? start there
|
|
723
|
+
return results.length > 0 ? [...results[0]] : [];
|
|
724
|
+
}
|
|
725
|
+
else if (typeof query.traceResponse === 'object') {
|
|
726
|
+
throw new Error('Objects are not yet supported');
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function resolveQueryPath(subject, queryPath, resultObjects) {
|
|
730
|
+
//start with the local instance as the subject
|
|
731
|
+
if (Array.isArray(queryPath)) {
|
|
732
|
+
//if the queryPath is an array of query steps, then resolve the query steps and let that convert the result
|
|
733
|
+
return resolveQuerySteps(subject, queryPath, resultObjects);
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
if (subject instanceof models_js_1.NamedNode) {
|
|
737
|
+
return evaluate(subject, queryPath);
|
|
738
|
+
}
|
|
739
|
+
return subject.map((node) => {
|
|
740
|
+
return evaluate(node, queryPath);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
function resolveQueryPathEndResults(subject, queryPath) {
|
|
745
|
+
//start with the local instance as the subject
|
|
746
|
+
let result = subject;
|
|
747
|
+
if (Array.isArray(queryPath)) {
|
|
748
|
+
for (let queryStep of queryPath) {
|
|
749
|
+
//then resolve each of the query steps and use the result as the new subject for the next step
|
|
750
|
+
result = resolveQueryStepEndResults(result, queryStep);
|
|
751
|
+
if (!result) {
|
|
752
|
+
break;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
else {
|
|
757
|
+
result = subject.map((singleNode) => {
|
|
758
|
+
return evaluate(singleNode, queryPath);
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
//return the final value at the end of the path
|
|
762
|
+
return result;
|
|
763
|
+
}
|
|
764
|
+
function evaluateWhere(node, method, args) {
|
|
765
|
+
let filterMethod;
|
|
766
|
+
if (method === SelectQuery_1.WhereMethods.EQUALS) {
|
|
767
|
+
filterMethod = resolveWhereEquals;
|
|
768
|
+
}
|
|
769
|
+
else if (method === SelectQuery_1.WhereMethods.SOME) {
|
|
770
|
+
filterMethod = resolveWhereSome;
|
|
771
|
+
}
|
|
772
|
+
else if (method === SelectQuery_1.WhereMethods.EVERY) {
|
|
773
|
+
filterMethod = resolveWhereEvery;
|
|
774
|
+
}
|
|
775
|
+
else {
|
|
776
|
+
throw new Error('Unimplemented where method: ' + method);
|
|
777
|
+
}
|
|
778
|
+
return filterMethod.apply(null, [node, ...args]);
|
|
779
|
+
}
|
|
780
|
+
function sortResults(subject, sortBy) {
|
|
781
|
+
if (subject instanceof models_js_1.NamedNode)
|
|
782
|
+
return subject;
|
|
783
|
+
//SORTING - how it works
|
|
784
|
+
//If a query is sorted by 2 paths (e.g. sort by lastName then by firstName), it will first sort by the first, then by the second if the first one didn't give a result
|
|
785
|
+
let ascending = sortBy.direction === 'ASC';
|
|
786
|
+
let sorted = [...subject].sort((a, b) => {
|
|
787
|
+
//go over each sort path (sortBy contains an array with 1 or more paths to sort by)
|
|
788
|
+
for (let sortPath of sortBy.paths) {
|
|
789
|
+
//resolve the value of the sort path for both a and b
|
|
790
|
+
let aValue = resolveQueryPathEndResults(a, sortPath);
|
|
791
|
+
let bValue = resolveQueryPathEndResults(b, sortPath);
|
|
792
|
+
//if the values are different, we can return the result
|
|
793
|
+
if (aValue < bValue) {
|
|
794
|
+
return ascending ? -1 : 1;
|
|
795
|
+
}
|
|
796
|
+
if (aValue > bValue) {
|
|
797
|
+
return ascending ? 1 : -1;
|
|
798
|
+
}
|
|
799
|
+
//else sort by the next path
|
|
800
|
+
}
|
|
801
|
+
//if we reach the end of the loop, then the values are equal by all paths
|
|
802
|
+
return 0;
|
|
803
|
+
});
|
|
804
|
+
return new NodeSet_js_1.NodeSet(sorted);
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Filters down the given subjects to only those what match the where clause
|
|
808
|
+
* @param subject
|
|
809
|
+
* @param where
|
|
810
|
+
* @private
|
|
811
|
+
*/
|
|
812
|
+
function filterResults(subject, where, resultObjects) {
|
|
813
|
+
// if ((where as WhereEvaluationPath).path) {
|
|
814
|
+
//for nested where clauses the subject will already be a QueryValue
|
|
815
|
+
//TODO: check if subject is ever not a shape, shapeset or string
|
|
816
|
+
//we're about to remove values from the subject set, so we need to clone it first so that we don't alter the graph
|
|
817
|
+
if (subject instanceof NodeSet_js_1.NodeSet) {
|
|
818
|
+
subject = subject.clone();
|
|
819
|
+
subject.forEach((node) => {
|
|
820
|
+
if (!evaluate(node, where)) {
|
|
821
|
+
resultObjects === null || resultObjects === void 0 ? void 0 : resultObjects.delete(node.uri);
|
|
822
|
+
subject.delete(node);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
return subject;
|
|
826
|
+
}
|
|
827
|
+
else if (subject instanceof models_js_1.NamedNode) {
|
|
828
|
+
return evaluate(subject, where)
|
|
829
|
+
? subject
|
|
830
|
+
: undefined;
|
|
831
|
+
}
|
|
832
|
+
else if (typeof subject === 'string') {
|
|
833
|
+
return evaluate(subject, where)
|
|
834
|
+
? subject
|
|
835
|
+
: undefined;
|
|
836
|
+
}
|
|
837
|
+
else if (typeof subject === 'undefined') {
|
|
838
|
+
//this can happen when comparing literals, and there is no value
|
|
839
|
+
return undefined;
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
throw Error('Unknown subject type: ' + subject);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Pre-processes the where clause to resolve the args if it is a path with args
|
|
847
|
+
* This prevents the need to resolve the args multiple times when evaluating the where clause
|
|
848
|
+
* @param where
|
|
849
|
+
*/
|
|
850
|
+
function preProcessWhere(where) {
|
|
851
|
+
//if the where clause is a path, we need to resolve the args
|
|
852
|
+
if (where.path && where.args) {
|
|
853
|
+
where.processedArgs = resolveWhereArgs(where.args);
|
|
854
|
+
return where.processedArgs;
|
|
855
|
+
}
|
|
856
|
+
return [];
|
|
857
|
+
}
|
|
858
|
+
function resolveWhereArgs(args) {
|
|
859
|
+
if (!args || !Array.isArray(args)) {
|
|
860
|
+
return [];
|
|
861
|
+
}
|
|
862
|
+
return args.map((arg) => {
|
|
863
|
+
//if this is an argpath
|
|
864
|
+
if (arg.path && !arg.args) {
|
|
865
|
+
//in this case we need to follow the path to the end value
|
|
866
|
+
if (!arg.subject) {
|
|
867
|
+
//if this happens, we probably need to NOT pre-process the where clause for args coming from the main query (as opposed to args from query context)
|
|
868
|
+
throw new Error('Expected a subject for arg path: ' + JSON.stringify(arg));
|
|
869
|
+
}
|
|
870
|
+
const node = models_js_1.NamedNode.getNamedNode(arg.subject.id);
|
|
871
|
+
if (!node) {
|
|
872
|
+
return [];
|
|
873
|
+
}
|
|
874
|
+
// const shapeClass = getShapeClass(node);
|
|
875
|
+
// const shape = (shapeClass as ShapeType).getFromURI((arg as ArgPath).subject.id) as Shape;
|
|
876
|
+
return resolveQueryPath(node, arg.path);
|
|
877
|
+
}
|
|
878
|
+
return arg;
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
function evaluate(singleNode, where) {
|
|
882
|
+
if (where.path) {
|
|
883
|
+
let shapeEndValue = resolveQueryPathEndResults(singleNode, where.path);
|
|
884
|
+
let args = where.processedArgs ||
|
|
885
|
+
preProcessWhere(where);
|
|
886
|
+
//when multiple values are the subject of the evaluation
|
|
887
|
+
//and, we're NOT evaluating some() or every()
|
|
888
|
+
if ((shapeEndValue instanceof NodeSet_js_1.NodeSet || Array.isArray(shapeEndValue)) &&
|
|
889
|
+
where.method !== SelectQuery_1.WhereMethods.SOME &&
|
|
890
|
+
where.method !== SelectQuery_1.WhereMethods.EVERY) {
|
|
891
|
+
//then by default we use some()
|
|
892
|
+
//that means, if any of the results matches the where clause, then the subject shape is returned
|
|
893
|
+
return shapeEndValue.some((singleEndValue) => {
|
|
894
|
+
return evaluateWhere(singleEndValue, where.method, args);
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return evaluateWhere(shapeEndValue, where.method, args);
|
|
898
|
+
}
|
|
899
|
+
else if (where.andOr) {
|
|
900
|
+
//the first run we simply take the result as the combined result
|
|
901
|
+
let initialResult = evaluate(singleNode, where.firstPath);
|
|
902
|
+
let booleanPaths = [initialResult];
|
|
903
|
+
where.andOr.forEach((andOr) => {
|
|
904
|
+
if (andOr.and) {
|
|
905
|
+
//if there is an and, we add the result of that and to the array
|
|
906
|
+
booleanPaths.push({ and: evaluate(singleNode, andOr.and) });
|
|
907
|
+
}
|
|
908
|
+
else if (andOr.or) {
|
|
909
|
+
//if there is an or, we add the result of that or to the array
|
|
910
|
+
booleanPaths.push({ or: evaluate(singleNode, andOr.or) });
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
//Say that we have: booleanPaths = [boolean,{and:boolean},{or:boolean},{and:boolean}]
|
|
914
|
+
//We should first process the AND: by combining the results of 0 & 1 and also 2 & 3
|
|
915
|
+
//So that it becomes: booleanPaths = [boolean,{or:boolean}]
|
|
916
|
+
var i = booleanPaths.length;
|
|
917
|
+
while (i--) {
|
|
918
|
+
let previous = booleanPaths[i - 1];
|
|
919
|
+
let current = booleanPaths[i];
|
|
920
|
+
if (typeof previous === 'undefined' || typeof current === 'undefined')
|
|
921
|
+
break;
|
|
922
|
+
//if the previous is a ShapeSet and the current is a ShapeSet, we combine them
|
|
923
|
+
if (current.hasOwnProperty('and')) {
|
|
924
|
+
if (previous.hasOwnProperty('and')) {
|
|
925
|
+
booleanPaths[i - 1].and =
|
|
926
|
+
previous.and && current.and;
|
|
927
|
+
}
|
|
928
|
+
else if (previous.hasOwnProperty('or')) {
|
|
929
|
+
booleanPaths[i - 1].or =
|
|
930
|
+
previous.or && current.and;
|
|
931
|
+
}
|
|
932
|
+
else if (typeof previous === 'boolean') {
|
|
933
|
+
booleanPaths[i - 1] = previous && current.and;
|
|
934
|
+
}
|
|
935
|
+
booleanPaths.splice(i, 1);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
//next we process the OR clauses
|
|
939
|
+
var i = booleanPaths.length;
|
|
940
|
+
while (i--) {
|
|
941
|
+
let previous = booleanPaths[i - 1];
|
|
942
|
+
let current = booleanPaths[i];
|
|
943
|
+
if (typeof previous === 'undefined' || typeof current === 'undefined')
|
|
944
|
+
break;
|
|
945
|
+
//for all or clauses, keep the results that are in either of the sets, so simply combine them
|
|
946
|
+
if (current.hasOwnProperty('or')) {
|
|
947
|
+
if (previous.hasOwnProperty('and')) {
|
|
948
|
+
booleanPaths[i - 1].and =
|
|
949
|
+
previous.and || current.or;
|
|
950
|
+
}
|
|
951
|
+
else if (previous.hasOwnProperty('or')) {
|
|
952
|
+
booleanPaths[i - 1].or =
|
|
953
|
+
previous.or || current.or;
|
|
954
|
+
}
|
|
955
|
+
else if (typeof previous === 'boolean') {
|
|
956
|
+
booleanPaths[i - 1] = previous || current.or;
|
|
957
|
+
}
|
|
958
|
+
//remove the current item from the array now that its processed
|
|
959
|
+
booleanPaths.splice(i, 1);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (booleanPaths.length > 1) {
|
|
963
|
+
throw new Error('booleanPaths should only have one item left: ' + booleanPaths.length);
|
|
964
|
+
}
|
|
965
|
+
//there should only be a single boolean left
|
|
966
|
+
return booleanPaths[0];
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
function resolveWhereEquals(queryEndValue, otherValue) {
|
|
970
|
+
if (queryEndValue instanceof models_js_1.NamedNode &&
|
|
971
|
+
otherValue.id) {
|
|
972
|
+
return queryEndValue.uri === otherValue.id;
|
|
973
|
+
}
|
|
974
|
+
return queryEndValue === otherValue;
|
|
975
|
+
}
|
|
976
|
+
function resolveWhereSome(nodes, evaluation) {
|
|
977
|
+
return nodes.some((node) => {
|
|
978
|
+
return evaluate(node, evaluation);
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
function resolveWhereEvery(nodes, evaluation) {
|
|
982
|
+
//there is an added check to see if there are any shapes
|
|
983
|
+
// because for example for this query where(p => p.friends.every(f => f.name.equals('Semmy')))
|
|
984
|
+
// it would be natural to expect that if there are no friends, the query would return false
|
|
985
|
+
return (nodes.size > 0 &&
|
|
986
|
+
nodes.every((node) => {
|
|
987
|
+
return evaluate(node, evaluation);
|
|
988
|
+
}));
|
|
989
|
+
}
|
|
990
|
+
function resolveQuerySteps(subject, queryPath, resultObjects) {
|
|
991
|
+
if (queryPath.length === 0) {
|
|
992
|
+
return subject;
|
|
993
|
+
}
|
|
994
|
+
//queryPath.slice(1,queryPath.length);
|
|
995
|
+
let [currentStep, ...restPath] = queryPath;
|
|
996
|
+
//if the first step is a ShapeReferenceValue, it comes from a QueryContextVariable
|
|
997
|
+
//and it serves as a replacement for the subject
|
|
998
|
+
if (currentStep.id &&
|
|
999
|
+
currentStep.shape) {
|
|
1000
|
+
// let shape = getShapeClass(NamedNode.getNamedNode((currentStep as ShapeReferenceValue).shape.id));
|
|
1001
|
+
// const shapeInstance = (shape as any).getFromURI((currentStep as ShapeReferenceValue).id) as Shape;
|
|
1002
|
+
// subject = shapeInstance;
|
|
1003
|
+
subject = models_js_1.NamedNode.getOrCreate(currentStep.id);
|
|
1004
|
+
//continue with the next step for this new subject
|
|
1005
|
+
[currentStep, ...restPath] = restPath;
|
|
1006
|
+
}
|
|
1007
|
+
if (subject instanceof models_js_1.NamedNode) {
|
|
1008
|
+
if (Array.isArray(currentStep)) {
|
|
1009
|
+
return resolveQueryPathsForNode(queryPath, subject, resultObjects);
|
|
1010
|
+
}
|
|
1011
|
+
//TODO: review differences between shape vs shapes and make it DRY
|
|
1012
|
+
return resolveQueryStepForNode(currentStep, subject, restPath, resultObjects);
|
|
1013
|
+
// } else if (subject instanceof CoreMap) {
|
|
1014
|
+
}
|
|
1015
|
+
else if (subject instanceof NodeSet_js_1.NodeSet) {
|
|
1016
|
+
if (Array.isArray(currentStep)) {
|
|
1017
|
+
resolveQueryPathsForNodes(currentStep, subject, restPath, resultObjects);
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
resolveQueryStepForNodes(currentStep, subject, resultObjects, restPath);
|
|
1021
|
+
}
|
|
1022
|
+
//return converted subjects
|
|
1023
|
+
return subject;
|
|
1024
|
+
//turn the map into an array of results
|
|
1025
|
+
// return [...resultObjects.values()];
|
|
1026
|
+
}
|
|
1027
|
+
else {
|
|
1028
|
+
throw new Error('Unknown subject type: ' + typeof subject);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
function shapeToResultObject(subject) {
|
|
1032
|
+
return {
|
|
1033
|
+
id: subject.uri,
|
|
1034
|
+
// shape: subject,
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
function namedNodeToResultObject(subject) {
|
|
1038
|
+
return {
|
|
1039
|
+
id: subject.uri,
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function literalNodeToResultObject(literal, property) {
|
|
1043
|
+
var _a;
|
|
1044
|
+
let datatype = property.datatype;
|
|
1045
|
+
let value = literal.value;
|
|
1046
|
+
if (datatype) {
|
|
1047
|
+
const dtId = (_a = datatype.id) !== null && _a !== void 0 ? _a : datatype;
|
|
1048
|
+
if (dtId === xsd_1.xsd.boolean.id) {
|
|
1049
|
+
return value === 'true';
|
|
1050
|
+
}
|
|
1051
|
+
else if (dtId === xsd_1.xsd.integer.id) {
|
|
1052
|
+
return parseInt(value);
|
|
1053
|
+
}
|
|
1054
|
+
else if (dtId === xsd_1.xsd.decimal.id || dtId === xsd_1.xsd.double.id) {
|
|
1055
|
+
return parseFloat(value);
|
|
1056
|
+
}
|
|
1057
|
+
else if (dtId === xsd_1.xsd.date.id || dtId === xsd_1.xsd.dateTime.id) {
|
|
1058
|
+
return new Date(value);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
//for other datatypes we just return the string value
|
|
1062
|
+
return value;
|
|
1063
|
+
}
|
|
1064
|
+
function nodesToResultObjects(subject) {
|
|
1065
|
+
//create the start of the result JS object for each subject node
|
|
1066
|
+
let resultObjects = new CoreMap_1.CoreMap();
|
|
1067
|
+
subject.forEach((sub) => {
|
|
1068
|
+
resultObjects.set(sub.uri, shapeToResultObject(sub));
|
|
1069
|
+
});
|
|
1070
|
+
return resultObjects;
|
|
1071
|
+
}
|
|
1072
|
+
function resolveQueryStepEndResults(subject, queryStep) {
|
|
1073
|
+
// if (subject instanceof NamedNode) {
|
|
1074
|
+
// if (Array.isArray(queryStep)) {
|
|
1075
|
+
// return resolveQueryPathsForNodeEndResults(queryStep, subject);
|
|
1076
|
+
// }
|
|
1077
|
+
// //TODO: review differences between shape vs shapes and make it DRY
|
|
1078
|
+
// return resolveQueryStepForNodeEndResults(queryStep, subject);
|
|
1079
|
+
// } else {
|
|
1080
|
+
// throw new Error('Unknown subject type: ' + typeof subject);
|
|
1081
|
+
// }
|
|
1082
|
+
if (subject instanceof models_js_1.NamedNode) {
|
|
1083
|
+
if (Array.isArray(queryStep)) {
|
|
1084
|
+
return resolveQueryPathsForNodeEndResults(queryStep, subject);
|
|
1085
|
+
}
|
|
1086
|
+
//TODO: review differences between shape vs shapes and make it DRY
|
|
1087
|
+
return resolveQueryStepForNodeEndResults(queryStep, subject);
|
|
1088
|
+
}
|
|
1089
|
+
if (subject instanceof NodeSet_js_1.NodeSet) {
|
|
1090
|
+
if (Array.isArray(queryStep)) {
|
|
1091
|
+
return resolveQueryPathsForNodesEndResults(queryStep, subject);
|
|
1092
|
+
}
|
|
1093
|
+
return resolveQueryStepForNodesEndResults(queryStep, subject);
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
throw new Error('Unknown subject type: ' + typeof subject);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function resolveQueryPathsForNodes(queryPaths, subjects, restPath, resultObjects) {
|
|
1100
|
+
let results = [];
|
|
1101
|
+
subjects.forEach((subject) => {
|
|
1102
|
+
let resultObject = resultObjects.get(subject.uri);
|
|
1103
|
+
let subjectResult = resolveQueryPathsForNode(queryPaths, subject, resultObject);
|
|
1104
|
+
let subResult = resolveQuerySteps(subjectResult, restPath, resultObject);
|
|
1105
|
+
results.push(subResult);
|
|
1106
|
+
});
|
|
1107
|
+
return results;
|
|
1108
|
+
}
|
|
1109
|
+
function resolveQueryPathsForNodesEndResults(queryPaths, subjects) {
|
|
1110
|
+
let results = [];
|
|
1111
|
+
subjects.forEach((subject) => {
|
|
1112
|
+
results.push(resolveQueryPathsForNodeEndResults(queryPaths, subject));
|
|
1113
|
+
});
|
|
1114
|
+
return results;
|
|
1115
|
+
}
|
|
1116
|
+
function resolveQueryPathsForNode(queryPaths, subject, resultObject) {
|
|
1117
|
+
if (Array.isArray(queryPaths)) {
|
|
1118
|
+
return queryPaths.map((queryPath) => {
|
|
1119
|
+
return resolveQueryPath(subject, queryPath, resultObject);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
else {
|
|
1123
|
+
throw new Error('TODO: implement support for custom query object: ' + queryPaths);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
function resolveQueryPathsForNodeEndResults(queryPaths, subject) {
|
|
1127
|
+
if (Array.isArray(queryPaths)) {
|
|
1128
|
+
return queryPaths.map((queryPath) => {
|
|
1129
|
+
return resolveQueryPathEndResults(subject, queryPath);
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
throw new Error('TODO: implement support for custom query object: ' + queryPaths);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
function resolveQueryStepForNode(queryStep, subject, restPath, resultObject) {
|
|
1137
|
+
if (queryStep.property) {
|
|
1138
|
+
return resolvePropertyStep(subject, queryStep, restPath, resultObject);
|
|
1139
|
+
}
|
|
1140
|
+
else if (queryStep.count) {
|
|
1141
|
+
return resolveCountStep(subject, queryStep, resultObject);
|
|
1142
|
+
}
|
|
1143
|
+
else if (queryStep.where) {
|
|
1144
|
+
throw new Error('Cannot filter a single shape');
|
|
1145
|
+
// } else if ((queryStep as BoundComponentQueryStep).component) {
|
|
1146
|
+
// return (queryStep as BoundComponentQueryStep).component.create(subject);
|
|
1147
|
+
}
|
|
1148
|
+
else if (typeof queryStep === 'object') {
|
|
1149
|
+
return resolveCustomObject(subject, queryStep, resultObject);
|
|
1150
|
+
}
|
|
1151
|
+
else {
|
|
1152
|
+
throw Error('Invalid query step: ' + queryStep);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
function resolveQueryStepForNodeEndResults(queryStep, subject) {
|
|
1156
|
+
if (queryStep.property) {
|
|
1157
|
+
let result = resolveQueryPropertyPath(subject, queryStep.property);
|
|
1158
|
+
if (queryStep.where) {
|
|
1159
|
+
result = filterResults(result, queryStep.where);
|
|
1160
|
+
}
|
|
1161
|
+
return result;
|
|
1162
|
+
}
|
|
1163
|
+
else if (queryStep.count) {
|
|
1164
|
+
return resolveCountStep(subject, queryStep);
|
|
1165
|
+
}
|
|
1166
|
+
else if (queryStep.where) {
|
|
1167
|
+
//in some cases there is a query step without property but WITH where
|
|
1168
|
+
//this happens when the where clause is on the root of the query
|
|
1169
|
+
//like Person.select(p => p.where(...))
|
|
1170
|
+
//in that case the where clause is directly applied to the given subject
|
|
1171
|
+
debugger;
|
|
1172
|
+
// let whereResult = resolveWhere(subject as ShapeSet, queryStep.where);
|
|
1173
|
+
// return whereResult;
|
|
1174
|
+
// } else if ((queryStep as BoundComponentQueryStep).component) {
|
|
1175
|
+
// return (queryStep as BoundComponentQueryStep).component.create(subject);
|
|
1176
|
+
// debugger;
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
throw Error('Invalid query step: ' + queryStep.toString());
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
function stepResultToSubResult(stepResult, property) {
|
|
1183
|
+
//TODO: review if this ever happens once we move away from relying on accessor implementation, review where this method is used
|
|
1184
|
+
// and if this code ever triggers
|
|
1185
|
+
if (stepResult instanceof NodeSet_js_1.NodeSet) {
|
|
1186
|
+
return nodesToResultObjects(stepResult);
|
|
1187
|
+
}
|
|
1188
|
+
// else if (stepResult instanceof Shape) {
|
|
1189
|
+
// return shapeToResultObject(stepResult);
|
|
1190
|
+
// }
|
|
1191
|
+
//temporary support for accessors returning named nodes
|
|
1192
|
+
else if (stepResult instanceof models_js_1.NamedNode) {
|
|
1193
|
+
return namedNodeToResultObject(stepResult);
|
|
1194
|
+
}
|
|
1195
|
+
else if (stepResult instanceof models_js_1.Literal) {
|
|
1196
|
+
return literalNodeToResultObject(stepResult, property);
|
|
1197
|
+
}
|
|
1198
|
+
else if (Array.isArray(stepResult)) {
|
|
1199
|
+
return stepResult.map((r) => stepResultToSubResult(r, property));
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
//strings,numbers,booleans,dates can just pass. but not other objects
|
|
1203
|
+
if (stepResult && typeof stepResult === 'object') {
|
|
1204
|
+
if (!(stepResult instanceof Date)) {
|
|
1205
|
+
console.warn('New warning, is this a warning? Unknown step result type: ', stepResult);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
return stepResult;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
function resolveQueryPropertyPath(node, property) {
|
|
1212
|
+
const singleValueProperty = property.maxCount === 1;
|
|
1213
|
+
let pathResult;
|
|
1214
|
+
let rawPath = property.path;
|
|
1215
|
+
let pathNodes = Array.isArray(rawPath)
|
|
1216
|
+
? rawPath.map(p => (0, toNamedNode_js_1.toNamedNode)(p))
|
|
1217
|
+
: [(0, toNamedNode_js_1.toNamedNode)(rawPath)];
|
|
1218
|
+
let lastProp = pathNodes.pop();
|
|
1219
|
+
let target = node;
|
|
1220
|
+
while (pathNodes.length > 0) {
|
|
1221
|
+
let prop = pathNodes.pop();
|
|
1222
|
+
target = target.getAll(prop);
|
|
1223
|
+
}
|
|
1224
|
+
if (singleValueProperty) {
|
|
1225
|
+
pathResult = convertLiteralToPrimitive(target.getOne(lastProp), property);
|
|
1226
|
+
}
|
|
1227
|
+
else {
|
|
1228
|
+
pathResult = target.getAll(lastProp);
|
|
1229
|
+
//if every value is a literal, we convert it to a plain array of plain/primitive values
|
|
1230
|
+
//if not, we keep using NodeSet, so we can more easily access sub paths from this potentially intermediate result.
|
|
1231
|
+
if (pathResult.every((n) => n instanceof models_js_1.Literal) && pathResult.size > 0) {
|
|
1232
|
+
pathResult = pathResult.map((v) => convertLiteralToPrimitive(v, property));
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
return pathResult;
|
|
1236
|
+
}
|
|
1237
|
+
function convertLiteralToPrimitive(node, property) {
|
|
1238
|
+
if (node instanceof models_js_1.Literal) {
|
|
1239
|
+
return literalNodeToResultObject(node, property);
|
|
1240
|
+
}
|
|
1241
|
+
return node;
|
|
1242
|
+
}
|
|
1243
|
+
function resolvePropertyStep(singleNode, queryStep, restPath, resultObjects) {
|
|
1244
|
+
//sometimes when .as() was used we may get a singleShape as subject that does not match with the nodeShape of the property of this step
|
|
1245
|
+
//If the singleShape does not match the nodeShape of the property, we change the shape
|
|
1246
|
+
// if(!singleNode.equals(queryStep.property.parentNodeShape.namedNode)) {
|
|
1247
|
+
// singleNode = new (getShapeClass(queryStep.property.parentNodeShape.namedNode) as any)(singleNode);
|
|
1248
|
+
// }
|
|
1249
|
+
//access the result on a node level
|
|
1250
|
+
let stepResult = resolveQueryPropertyPath(singleNode, queryStep.property);
|
|
1251
|
+
//directly access the get/set method of the shape
|
|
1252
|
+
// let stepResult = singleShape[(queryStep as PropertyQueryStep).property.label];
|
|
1253
|
+
let subResultObjects = stepResultToSubResult(stepResult, queryStep.property);
|
|
1254
|
+
if (queryStep.where) {
|
|
1255
|
+
stepResult = filterResults(stepResult, queryStep.where, subResultObjects);
|
|
1256
|
+
//if the result is empty, then the shape didn't make it through the filter and needs to be removed from the results
|
|
1257
|
+
// if (typeof stepResult === 'undefined' || stepResult === null) {
|
|
1258
|
+
// resultObjects.delete(singleShape.uri);
|
|
1259
|
+
// return;
|
|
1260
|
+
// }
|
|
1261
|
+
//if the filtered result is null or undefined, then we don't need to add it to the result object
|
|
1262
|
+
if (typeof stepResult === 'undefined' || stepResult === null) {
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (restPath.length > 0 && typeof stepResult !== 'undefined') {
|
|
1267
|
+
//if there is more properties left, continue to fill the result object by resolving the next steps
|
|
1268
|
+
stepResult = resolveQuerySteps(stepResult, restPath, subResultObjects);
|
|
1269
|
+
}
|
|
1270
|
+
//TODO: refactor/review this code - although it works and its inticrate, its moved around
|
|
1271
|
+
// just change names so its more clear
|
|
1272
|
+
//This converts the subResultObjects into the step result, but are there always "subResultObjects"?
|
|
1273
|
+
//Moved this outside the if because customObject results also need this (so we return an array of result objects, not a nodeset)
|
|
1274
|
+
stepResult =
|
|
1275
|
+
subResultObjects instanceof Map
|
|
1276
|
+
? [...subResultObjects.values()]
|
|
1277
|
+
: subResultObjects;
|
|
1278
|
+
if (typeof resultObjects !== 'undefined') {
|
|
1279
|
+
// }
|
|
1280
|
+
// if (stepResult instanceof ShapeSet) {
|
|
1281
|
+
// stepResult = [...subResultObjects.values()];
|
|
1282
|
+
// }
|
|
1283
|
+
// if (stepResult instanceof Shape) {
|
|
1284
|
+
// stepResult = subResultObjects;
|
|
1285
|
+
// }
|
|
1286
|
+
//get the current result object for this shape
|
|
1287
|
+
// if (typeof resultObjects !== 'undefined') {
|
|
1288
|
+
let nodeResult = resultObjects instanceof Map
|
|
1289
|
+
? resultObjects.get(singleNode.uri)
|
|
1290
|
+
: resultObjects;
|
|
1291
|
+
//write the result for this property into the result object
|
|
1292
|
+
writeResultObject(nodeResult, queryStep.property.label, stepResult);
|
|
1293
|
+
// nodeResult[(queryStep as PropertyQueryStep).property.label] = stepResult;
|
|
1294
|
+
return subResultObjects ? nodeResult : stepResult;
|
|
1295
|
+
}
|
|
1296
|
+
// nodeResult[(queryStep as PropertyQueryStep).property.label] = subResultObjects
|
|
1297
|
+
// ? subResultObjects instanceof Map
|
|
1298
|
+
// ? [...subResultObjects.values()]
|
|
1299
|
+
// : subResultObjects
|
|
1300
|
+
// : stepResult;
|
|
1301
|
+
// return stepResult;
|
|
1302
|
+
return stepResult;
|
|
1303
|
+
// resultObjects
|
|
1304
|
+
// ? resultObjects instanceof Map
|
|
1305
|
+
// ? [...resultObjects.values()]
|
|
1306
|
+
// : resultObjects
|
|
1307
|
+
// : stepResult;
|
|
1308
|
+
}
|
|
1309
|
+
function resolveCountStep(singleNode, queryStep, resultObjects) {
|
|
1310
|
+
//We use the flat version of resolveQuerySteps here, because we don't need QResult objects here
|
|
1311
|
+
// we're only interested in the final results
|
|
1312
|
+
let countable = resolveQueryPathEndResults(singleNode, queryStep.count);
|
|
1313
|
+
let result;
|
|
1314
|
+
if (Array.isArray(countable)) {
|
|
1315
|
+
result = countable.length;
|
|
1316
|
+
}
|
|
1317
|
+
else if (countable instanceof Set) {
|
|
1318
|
+
result = countable.size;
|
|
1319
|
+
}
|
|
1320
|
+
else {
|
|
1321
|
+
throw Error('Not sure how to count this: ' + countable.toString());
|
|
1322
|
+
}
|
|
1323
|
+
updateResultObjects(singleNode, queryStep, result, resultObjects, 'count');
|
|
1324
|
+
return result;
|
|
1325
|
+
}
|
|
1326
|
+
function updateResultObjects(node, queryStep, result, resultObjects, defaultLabel) {
|
|
1327
|
+
if (resultObjects) {
|
|
1328
|
+
let nodeResult = resultObjects instanceof Map
|
|
1329
|
+
? resultObjects.get(node.uri)
|
|
1330
|
+
: resultObjects;
|
|
1331
|
+
if (nodeResult) {
|
|
1332
|
+
writeResultObject(nodeResult, queryStep.label || defaultLabel, result);
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function resolveQueryStepForNodes(queryStep, subject, resultObjects, restPath) {
|
|
1337
|
+
if (queryStep.property) {
|
|
1338
|
+
subject.forEach((singleNode) => {
|
|
1339
|
+
resolvePropertyStep(singleNode, queryStep, restPath, resultObjects);
|
|
1340
|
+
});
|
|
1341
|
+
// return result;
|
|
1342
|
+
}
|
|
1343
|
+
else if (queryStep.count) {
|
|
1344
|
+
//count the countable
|
|
1345
|
+
subject.forEach((singleNode) => {
|
|
1346
|
+
resolveCountStep(singleNode, queryStep, resultObjects);
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
else if (queryStep.where) {
|
|
1350
|
+
//in some cases there is a query step without property but WITH where
|
|
1351
|
+
//this happens when the where clause is on the root of the query
|
|
1352
|
+
//like Person.select(p => p.where(...))
|
|
1353
|
+
//in that case the where clause is directly applied to the given subject
|
|
1354
|
+
subject = filterResults(subject, queryStep.where, resultObjects);
|
|
1355
|
+
if (restPath.length > 0) {
|
|
1356
|
+
//if there is more properties left, continue to fill the result object by resolving the next steps
|
|
1357
|
+
resolveQuerySteps(subject, restPath, resultObjects);
|
|
1358
|
+
}
|
|
1359
|
+
// return whereResult;
|
|
1360
|
+
}
|
|
1361
|
+
else if (typeof queryStep === 'object') {
|
|
1362
|
+
subject.forEach((singleShape) => {
|
|
1363
|
+
resolveCustomObject(singleShape, queryStep, resultObjects ? resultObjects.get(singleShape.uri) : null);
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function resolveQueryStepForNodesEndResults(queryStep, subject) {
|
|
1368
|
+
var _a;
|
|
1369
|
+
if (queryStep.property) {
|
|
1370
|
+
//if the propertyshape states that it only accepts literal values in the graph,
|
|
1371
|
+
// then the result will be an Array
|
|
1372
|
+
let result = ((_a = queryStep.property.nodeKind) === null || _a === void 0 ? void 0 : _a.id) === shacl_1.shacl.Literal.id ||
|
|
1373
|
+
queryStep.count
|
|
1374
|
+
? []
|
|
1375
|
+
: new NodeSet_js_1.NodeSet();
|
|
1376
|
+
subject.forEach((singleNode) => {
|
|
1377
|
+
// //directly access the get/set method of the shape
|
|
1378
|
+
// let stepResult =
|
|
1379
|
+
// singleNode[(queryStep as PropertyQueryStep).property.label];
|
|
1380
|
+
// let stepResult:NodeSet<NamedNode>|NamedNode[]|NamedNode|number = getPropertyPath(singleNode,(queryStep as PropertyQueryStep).property.path);
|
|
1381
|
+
let stepResult = resolveQueryPropertyPath(singleNode, queryStep.property);
|
|
1382
|
+
if (queryStep.where) {
|
|
1383
|
+
stepResult = filterResults(stepResult, queryStep.where);
|
|
1384
|
+
}
|
|
1385
|
+
if (queryStep.count) {
|
|
1386
|
+
if (Array.isArray(stepResult)) {
|
|
1387
|
+
stepResult = stepResult.length;
|
|
1388
|
+
}
|
|
1389
|
+
else if (stepResult instanceof Set) {
|
|
1390
|
+
stepResult = stepResult.size;
|
|
1391
|
+
}
|
|
1392
|
+
else {
|
|
1393
|
+
throw Error('Not sure how to count this: ' + stepResult.toString());
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
if (typeof stepResult === 'undefined' || stepResult === null) {
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (stepResult instanceof NodeSet_js_1.NodeSet) {
|
|
1400
|
+
stepResult = [...stepResult];
|
|
1401
|
+
}
|
|
1402
|
+
if (Array.isArray(stepResult)) {
|
|
1403
|
+
result = result.concat(stepResult);
|
|
1404
|
+
}
|
|
1405
|
+
else if (stepResult instanceof models_js_1.NamedNode) {
|
|
1406
|
+
result.add(stepResult);
|
|
1407
|
+
}
|
|
1408
|
+
else if (primitiveTypes.includes(typeof stepResult)) {
|
|
1409
|
+
result.push(stepResult);
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
throw Error('Unknown result type: ' +
|
|
1413
|
+
typeof stepResult +
|
|
1414
|
+
' for property ' +
|
|
1415
|
+
queryStep.property.label +
|
|
1416
|
+
' on shape ' +
|
|
1417
|
+
singleNode.toString() +
|
|
1418
|
+
')');
|
|
1419
|
+
}
|
|
1420
|
+
});
|
|
1421
|
+
return result;
|
|
1422
|
+
}
|
|
1423
|
+
else if (queryStep.where) {
|
|
1424
|
+
//in some cases there is a query step without property but WITH where
|
|
1425
|
+
//this happens when the where clause is on the root of the query
|
|
1426
|
+
//like Person.select(p => p.where(...))
|
|
1427
|
+
//in that case the where clause is directly applied to the given subject
|
|
1428
|
+
let whereResult = filterResults(subject, queryStep.where);
|
|
1429
|
+
return whereResult;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
function XSDDate_fromNativeDate(nativeDate, datatype) {
|
|
1433
|
+
if (!nativeDate)
|
|
1434
|
+
return null;
|
|
1435
|
+
var value = nativeDate.toISOString();
|
|
1436
|
+
let literal = new models_js_1.Literal(value, (0, toNamedNode_js_1.toNamedNode)(datatype));
|
|
1437
|
+
return literal;
|
|
1438
|
+
}
|
|
1439
|
+
function Boolean_toLiteral(value) {
|
|
1440
|
+
return new models_js_1.Literal(value.toString(), (0, toNamedNode_js_1.toNamedNode)(xsd_1.xsd.boolean));
|
|
1441
|
+
}
|
|
1442
|
+
//# sourceMappingURL=LocalQueryResolver.js.map
|