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