@undefineds.co/drizzle-solid 0.3.0 → 0.3.2

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 (245) hide show
  1. package/CHANGELOG-DRAFT.md +1 -1
  2. package/README.md +128 -144
  3. package/README.zh-CN.md +254 -0
  4. package/dist/core/ast-to-sparql.d.ts +31 -24
  5. package/dist/core/ast-to-sparql.d.ts.map +1 -1
  6. package/dist/core/ast-to-sparql.js.map +1 -1
  7. package/dist/core/compile-time-types.d.ts +10 -7
  8. package/dist/core/compile-time-types.d.ts.map +1 -1
  9. package/dist/core/compile-time-types.js +6 -4
  10. package/dist/core/compile-time-types.js.map +1 -1
  11. package/dist/core/comunica-patch.d.ts.map +1 -1
  12. package/dist/core/comunica-patch.js +0 -1
  13. package/dist/core/comunica-patch.js.map +1 -1
  14. package/dist/core/conflict-resolution.d.ts.map +1 -1
  15. package/dist/core/conflict-resolution.js +3 -2
  16. package/dist/core/conflict-resolution.js.map +1 -1
  17. package/dist/core/discovery/typeindex-discovery.d.ts.map +1 -1
  18. package/dist/core/discovery/typeindex-discovery.js +0 -2
  19. package/dist/core/discovery/typeindex-discovery.js.map +1 -1
  20. package/dist/core/execution/ldp-executor.d.ts.map +1 -1
  21. package/dist/core/execution/ldp-executor.js +2 -21
  22. package/dist/core/execution/ldp-executor.js.map +1 -1
  23. package/dist/core/execution/pod-executor.d.ts.map +1 -1
  24. package/dist/core/execution/pod-executor.js +2 -4
  25. package/dist/core/execution/pod-executor.js.map +1 -1
  26. package/dist/core/execution/sparql-strategy.d.ts +0 -4
  27. package/dist/core/execution/sparql-strategy.d.ts.map +1 -1
  28. package/dist/core/execution/sparql-strategy.js +6 -64
  29. package/dist/core/execution/sparql-strategy.js.map +1 -1
  30. package/dist/core/expressions.d.ts +10 -9
  31. package/dist/core/expressions.d.ts.map +1 -1
  32. package/dist/core/expressions.js.map +1 -1
  33. package/dist/core/pod-database.d.ts +59 -15
  34. package/dist/core/pod-database.d.ts.map +1 -1
  35. package/dist/core/pod-database.js +99 -35
  36. package/dist/core/pod-database.js.map +1 -1
  37. package/dist/core/pod-dialect.d.ts +5 -1
  38. package/dist/core/pod-dialect.d.ts.map +1 -1
  39. package/dist/core/pod-dialect.js +32 -27
  40. package/dist/core/pod-dialect.js.map +1 -1
  41. package/dist/core/pod-session.d.ts +10 -9
  42. package/dist/core/pod-session.d.ts.map +1 -1
  43. package/dist/core/pod-session.js.map +1 -1
  44. package/dist/core/query-builders/delete-query-builder.d.ts +2 -2
  45. package/dist/core/query-builders/delete-query-builder.d.ts.map +1 -1
  46. package/dist/core/query-builders/delete-query-builder.js +8 -6
  47. package/dist/core/query-builders/delete-query-builder.js.map +1 -1
  48. package/dist/core/query-builders/select-query-builder.d.ts +13 -4
  49. package/dist/core/query-builders/select-query-builder.d.ts.map +1 -1
  50. package/dist/core/query-builders/select-query-builder.js +207 -21
  51. package/dist/core/query-builders/select-query-builder.js.map +1 -1
  52. package/dist/core/query-builders/update-query-builder.d.ts +2 -2
  53. package/dist/core/query-builders/update-query-builder.d.ts.map +1 -1
  54. package/dist/core/query-builders/update-query-builder.js +7 -5
  55. package/dist/core/query-builders/update-query-builder.js.map +1 -1
  56. package/dist/core/query-conditions.d.ts +18 -11
  57. package/dist/core/query-conditions.d.ts.map +1 -1
  58. package/dist/core/query-conditions.js.map +1 -1
  59. package/dist/core/query-where-policy.d.ts +7 -0
  60. package/dist/core/query-where-policy.d.ts.map +1 -0
  61. package/dist/core/query-where-policy.js +83 -0
  62. package/dist/core/query-where-policy.js.map +1 -0
  63. package/dist/core/resource-resolver/base-resolver.d.ts +1 -0
  64. package/dist/core/resource-resolver/base-resolver.d.ts.map +1 -1
  65. package/dist/core/resource-resolver/base-resolver.js +33 -39
  66. package/dist/core/resource-resolver/base-resolver.js.map +1 -1
  67. package/dist/core/resource-resolver/document-resolver.d.ts +2 -1
  68. package/dist/core/resource-resolver/document-resolver.d.ts.map +1 -1
  69. package/dist/core/resource-resolver/document-resolver.js +17 -46
  70. package/dist/core/resource-resolver/document-resolver.js.map +1 -1
  71. package/dist/core/runtime/pod-runtime.d.ts.map +1 -1
  72. package/dist/core/runtime/pod-runtime.js +0 -6
  73. package/dist/core/runtime/pod-runtime.js.map +1 -1
  74. package/dist/core/select-plan.d.ts +9 -7
  75. package/dist/core/select-plan.d.ts.map +1 -1
  76. package/dist/core/solid-n3-client.d.ts.map +1 -1
  77. package/dist/core/solid-n3-client.js +20 -9
  78. package/dist/core/solid-n3-client.js.map +1 -1
  79. package/dist/core/sparql/builder/expression-builder.d.ts +9 -11
  80. package/dist/core/sparql/builder/expression-builder.d.ts.map +1 -1
  81. package/dist/core/sparql/builder/expression-builder.js +162 -59
  82. package/dist/core/sparql/builder/expression-builder.js.map +1 -1
  83. package/dist/core/sparql/builder/select-builder.d.ts.map +1 -1
  84. package/dist/core/sparql/builder/select-builder.js +18 -4
  85. package/dist/core/sparql/builder/select-builder.js.map +1 -1
  86. package/dist/core/sparql/builder/update-builder.d.ts.map +1 -1
  87. package/dist/core/sparql/builder/update-builder.js +0 -11
  88. package/dist/core/sparql/builder/update-builder.js.map +1 -1
  89. package/dist/core/sparql-engine.d.ts +1 -1
  90. package/dist/core/sparql-engine.d.ts.map +1 -1
  91. package/dist/core/sparql-executor.d.ts +9 -6
  92. package/dist/core/sparql-executor.d.ts.map +1 -1
  93. package/dist/core/sparql-executor.js +53 -43
  94. package/dist/core/sparql-executor.js.map +1 -1
  95. package/dist/core/typeindex-manager.d.ts.map +1 -1
  96. package/dist/core/typeindex-manager.js +3 -0
  97. package/dist/core/typeindex-manager.js.map +1 -1
  98. package/dist/core/types.d.ts +3 -3
  99. package/dist/core/types.d.ts.map +1 -1
  100. package/dist/core/zod-integration.d.ts +9 -7
  101. package/dist/core/zod-integration.d.ts.map +1 -1
  102. package/dist/core/zod-integration.js.map +1 -1
  103. package/dist/esm/core/ast-to-sparql.d.ts +31 -24
  104. package/dist/esm/core/ast-to-sparql.d.ts.map +1 -1
  105. package/dist/esm/core/ast-to-sparql.js.map +1 -1
  106. package/dist/esm/core/compile-time-types.d.ts +10 -7
  107. package/dist/esm/core/compile-time-types.d.ts.map +1 -1
  108. package/dist/esm/core/compile-time-types.js +6 -4
  109. package/dist/esm/core/compile-time-types.js.map +1 -1
  110. package/dist/esm/core/comunica-patch.d.ts.map +1 -1
  111. package/dist/esm/core/comunica-patch.js +0 -1
  112. package/dist/esm/core/comunica-patch.js.map +1 -1
  113. package/dist/esm/core/conflict-resolution.d.ts.map +1 -1
  114. package/dist/esm/core/conflict-resolution.js +3 -2
  115. package/dist/esm/core/conflict-resolution.js.map +1 -1
  116. package/dist/esm/core/discovery/typeindex-discovery.d.ts.map +1 -1
  117. package/dist/esm/core/discovery/typeindex-discovery.js +0 -2
  118. package/dist/esm/core/discovery/typeindex-discovery.js.map +1 -1
  119. package/dist/esm/core/execution/ldp-executor.d.ts.map +1 -1
  120. package/dist/esm/core/execution/ldp-executor.js +2 -21
  121. package/dist/esm/core/execution/ldp-executor.js.map +1 -1
  122. package/dist/esm/core/execution/pod-executor.d.ts.map +1 -1
  123. package/dist/esm/core/execution/pod-executor.js +2 -4
  124. package/dist/esm/core/execution/pod-executor.js.map +1 -1
  125. package/dist/esm/core/execution/sparql-strategy.d.ts +0 -4
  126. package/dist/esm/core/execution/sparql-strategy.d.ts.map +1 -1
  127. package/dist/esm/core/execution/sparql-strategy.js +7 -65
  128. package/dist/esm/core/execution/sparql-strategy.js.map +1 -1
  129. package/dist/esm/core/expressions.d.ts +10 -9
  130. package/dist/esm/core/expressions.d.ts.map +1 -1
  131. package/dist/esm/core/expressions.js.map +1 -1
  132. package/dist/esm/core/pod-database.d.ts +59 -15
  133. package/dist/esm/core/pod-database.d.ts.map +1 -1
  134. package/dist/esm/core/pod-database.js +99 -35
  135. package/dist/esm/core/pod-database.js.map +1 -1
  136. package/dist/esm/core/pod-dialect.d.ts +5 -1
  137. package/dist/esm/core/pod-dialect.d.ts.map +1 -1
  138. package/dist/esm/core/pod-dialect.js +33 -28
  139. package/dist/esm/core/pod-dialect.js.map +1 -1
  140. package/dist/esm/core/pod-session.d.ts +10 -9
  141. package/dist/esm/core/pod-session.d.ts.map +1 -1
  142. package/dist/esm/core/pod-session.js.map +1 -1
  143. package/dist/esm/core/query-builders/delete-query-builder.d.ts +2 -2
  144. package/dist/esm/core/query-builders/delete-query-builder.d.ts.map +1 -1
  145. package/dist/esm/core/query-builders/delete-query-builder.js +8 -6
  146. package/dist/esm/core/query-builders/delete-query-builder.js.map +1 -1
  147. package/dist/esm/core/query-builders/select-query-builder.d.ts +13 -4
  148. package/dist/esm/core/query-builders/select-query-builder.d.ts.map +1 -1
  149. package/dist/esm/core/query-builders/select-query-builder.js +208 -22
  150. package/dist/esm/core/query-builders/select-query-builder.js.map +1 -1
  151. package/dist/esm/core/query-builders/update-query-builder.d.ts +2 -2
  152. package/dist/esm/core/query-builders/update-query-builder.d.ts.map +1 -1
  153. package/dist/esm/core/query-builders/update-query-builder.js +7 -5
  154. package/dist/esm/core/query-builders/update-query-builder.js.map +1 -1
  155. package/dist/esm/core/query-conditions.d.ts +18 -11
  156. package/dist/esm/core/query-conditions.d.ts.map +1 -1
  157. package/dist/esm/core/query-conditions.js.map +1 -1
  158. package/dist/esm/core/query-where-policy.d.ts +7 -0
  159. package/dist/esm/core/query-where-policy.d.ts.map +1 -0
  160. package/dist/esm/core/query-where-policy.js +78 -0
  161. package/dist/esm/core/query-where-policy.js.map +1 -0
  162. package/dist/esm/core/resource-resolver/base-resolver.d.ts +1 -0
  163. package/dist/esm/core/resource-resolver/base-resolver.d.ts.map +1 -1
  164. package/dist/esm/core/resource-resolver/base-resolver.js +33 -39
  165. package/dist/esm/core/resource-resolver/base-resolver.js.map +1 -1
  166. package/dist/esm/core/resource-resolver/document-resolver.d.ts +2 -1
  167. package/dist/esm/core/resource-resolver/document-resolver.d.ts.map +1 -1
  168. package/dist/esm/core/resource-resolver/document-resolver.js +17 -46
  169. package/dist/esm/core/resource-resolver/document-resolver.js.map +1 -1
  170. package/dist/esm/core/runtime/pod-runtime.d.ts.map +1 -1
  171. package/dist/esm/core/runtime/pod-runtime.js +0 -6
  172. package/dist/esm/core/runtime/pod-runtime.js.map +1 -1
  173. package/dist/esm/core/select-plan.d.ts +9 -7
  174. package/dist/esm/core/select-plan.d.ts.map +1 -1
  175. package/dist/esm/core/solid-n3-client.d.ts.map +1 -1
  176. package/dist/esm/core/solid-n3-client.js +20 -9
  177. package/dist/esm/core/solid-n3-client.js.map +1 -1
  178. package/dist/esm/core/sparql/builder/expression-builder.d.ts +9 -11
  179. package/dist/esm/core/sparql/builder/expression-builder.d.ts.map +1 -1
  180. package/dist/esm/core/sparql/builder/expression-builder.js +162 -59
  181. package/dist/esm/core/sparql/builder/expression-builder.js.map +1 -1
  182. package/dist/esm/core/sparql/builder/select-builder.d.ts.map +1 -1
  183. package/dist/esm/core/sparql/builder/select-builder.js +18 -4
  184. package/dist/esm/core/sparql/builder/select-builder.js.map +1 -1
  185. package/dist/esm/core/sparql/builder/update-builder.d.ts.map +1 -1
  186. package/dist/esm/core/sparql/builder/update-builder.js +0 -11
  187. package/dist/esm/core/sparql/builder/update-builder.js.map +1 -1
  188. package/dist/esm/core/sparql-engine.d.ts +1 -1
  189. package/dist/esm/core/sparql-engine.d.ts.map +1 -1
  190. package/dist/esm/core/sparql-executor.d.ts +9 -6
  191. package/dist/esm/core/sparql-executor.d.ts.map +1 -1
  192. package/dist/esm/core/sparql-executor.js +53 -43
  193. package/dist/esm/core/sparql-executor.js.map +1 -1
  194. package/dist/esm/core/typeindex-manager.d.ts.map +1 -1
  195. package/dist/esm/core/typeindex-manager.js +3 -0
  196. package/dist/esm/core/typeindex-manager.js.map +1 -1
  197. package/dist/esm/core/types.d.ts +3 -3
  198. package/dist/esm/core/types.d.ts.map +1 -1
  199. package/dist/esm/core/zod-integration.d.ts +9 -7
  200. package/dist/esm/core/zod-integration.d.ts.map +1 -1
  201. package/dist/esm/core/zod-integration.js.map +1 -1
  202. package/dist/esm/index.d.ts +1 -1
  203. package/dist/esm/index.d.ts.map +1 -1
  204. package/dist/esm/index.js +1 -1
  205. package/dist/esm/index.js.map +1 -1
  206. package/dist/esm/pod.d.ts +53 -3
  207. package/dist/esm/pod.d.ts.map +1 -1
  208. package/dist/esm/utils/find-by-iri.d.ts +4 -2
  209. package/dist/esm/utils/find-by-iri.d.ts.map +1 -1
  210. package/dist/esm/utils/find-by-iri.js +8 -8
  211. package/dist/esm/utils/find-by-iri.js.map +1 -1
  212. package/dist/esm/utils/helpers.d.ts +2 -2
  213. package/dist/esm/utils/helpers.d.ts.map +1 -1
  214. package/dist/esm/utils/rdf-helpers.d.ts +2 -2
  215. package/dist/esm/utils/rdf-helpers.d.ts.map +1 -1
  216. package/dist/esm/utils/rdf-helpers.js +9 -6
  217. package/dist/esm/utils/rdf-helpers.js.map +1 -1
  218. package/dist/esm/utils/rdf-validation.d.ts +5 -3
  219. package/dist/esm/utils/rdf-validation.d.ts.map +1 -1
  220. package/dist/esm/utils/rdf-validation.js +47 -8
  221. package/dist/esm/utils/rdf-validation.js.map +1 -1
  222. package/dist/esm/utils/webid-resolver.js +1 -1
  223. package/dist/esm/utils/webid-resolver.js.map +1 -1
  224. package/dist/index.d.ts +1 -1
  225. package/dist/index.d.ts.map +1 -1
  226. package/dist/index.js.map +1 -1
  227. package/dist/pod.d.ts +53 -3
  228. package/dist/pod.d.ts.map +1 -1
  229. package/dist/utils/find-by-iri.d.ts +4 -2
  230. package/dist/utils/find-by-iri.d.ts.map +1 -1
  231. package/dist/utils/find-by-iri.js +8 -8
  232. package/dist/utils/find-by-iri.js.map +1 -1
  233. package/dist/utils/helpers.d.ts +2 -2
  234. package/dist/utils/helpers.d.ts.map +1 -1
  235. package/dist/utils/rdf-helpers.d.ts +2 -2
  236. package/dist/utils/rdf-helpers.d.ts.map +1 -1
  237. package/dist/utils/rdf-helpers.js +9 -6
  238. package/dist/utils/rdf-helpers.js.map +1 -1
  239. package/dist/utils/rdf-validation.d.ts +5 -3
  240. package/dist/utils/rdf-validation.d.ts.map +1 -1
  241. package/dist/utils/rdf-validation.js +47 -8
  242. package/dist/utils/rdf-validation.js.map +1 -1
  243. package/dist/utils/webid-resolver.js +1 -1
  244. package/dist/utils/webid-resolver.js.map +1 -1
  245. package/package.json +3 -2
@@ -10,6 +10,7 @@ const helpers_1 = require("./helpers");
10
10
  const uri_1 = require("../uri");
11
11
  const order_by_1 = require("../order-by");
12
12
  const expressions_1 = require("../expressions");
13
+ const query_where_policy_1 = require("../query-where-policy");
13
14
  class SelectQueryBuilder {
14
15
  constructor(session, fields) {
15
16
  this.session = session;
@@ -40,23 +41,19 @@ class SelectQueryBuilder {
40
41
  /**
41
42
  * Add WHERE conditions to the query
42
43
  *
43
- * Note: Using '@id' directly in conditions is deprecated.
44
- * Use db.findByIri(table, iri) for IRI-based lookups instead.
44
+ * Public where() is collection-oriented.
45
+ * Exact-target reads must use findByLocator()/findByIri().
45
46
  */
46
47
  where(conditions) {
47
48
  if (conditions instanceof drizzle_orm_1.SQL) {
48
49
  this.sql = conditions;
49
50
  }
50
51
  else if (this.isQueryCondition(conditions)) {
52
+ (0, query_where_policy_1.assertPublicWhereCondition)('select', conditions);
51
53
  this.processQueryCondition(conditions);
52
54
  }
53
55
  else {
54
- // Check for @id usage and warn/reject
55
- if (conditions && typeof conditions === 'object' && '@id' in conditions) {
56
- throw new Error(`Using '@id' in where() is not supported. ` +
57
- `Use db.findByIri(table, iri) for IRI-based lookups, ` +
58
- `or use { id: 'value' } for id-based lookups.`);
59
- }
56
+ (0, query_where_policy_1.assertPublicWhereObject)('select', conditions);
60
57
  this.processWhereObject(conditions);
61
58
  }
62
59
  return this;
@@ -304,6 +301,10 @@ class SelectQueryBuilder {
304
301
  this.whereConditions = simpleConditions;
305
302
  }
306
303
  }
304
+ applyInternalQueryCondition(condition) {
305
+ this.processQueryCondition(condition);
306
+ return this;
307
+ }
307
308
  normalizeWhereConditions() {
308
309
  if (this.conditionTree) {
309
310
  return this.conditionTree;
@@ -457,9 +458,12 @@ class SelectQueryBuilder {
457
458
  limit: undefined,
458
459
  offset: undefined,
459
460
  orderBy: undefined,
461
+ ...(hasJoins ? { joins: undefined, joinFilters: undefined } : {}),
460
462
  ...(useAggregateFallback ? { groupBy: undefined, having: undefined } : {}),
461
463
  }
462
- : plan;
464
+ : (hasJoins
465
+ ? { ...plan, joins: undefined, joinFilters: undefined }
466
+ : plan);
463
467
  const operation = {
464
468
  type: 'select',
465
469
  table: this.selectedTable,
@@ -497,6 +501,7 @@ class SelectQueryBuilder {
497
501
  }
498
502
  if (hasJoins) {
499
503
  intermediateRows = this.normalizeBaseRows(intermediateRows);
504
+ intermediateRows = this.mergeRowsBySubject(intermediateRows);
500
505
  intermediateRows = await this.applyJoinFallback(intermediateRows);
501
506
  intermediateRows = this.applyJoinFilters(intermediateRows);
502
507
  }
@@ -913,10 +918,19 @@ class SelectQueryBuilder {
913
918
  applySubjectMetadata(rows) {
914
919
  return rows.map((row) => this.attachSubjectMetadata(row));
915
920
  }
921
+ extractSubjectValue(row) {
922
+ const candidate = row.subject ?? row['@id'] ?? row.uri;
923
+ if (typeof candidate === 'object' && candidate && 'value' in candidate) {
924
+ const value = candidate.value;
925
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
926
+ }
927
+ if (typeof candidate === 'string' && candidate.length > 0) {
928
+ return candidate;
929
+ }
930
+ return undefined;
931
+ }
916
932
  attachSubjectMetadata(row) {
917
- const subjectValue = typeof row.subject === 'object' && row.subject && 'value' in row.subject
918
- ? row.subject.value
919
- : row.subject;
933
+ const subjectValue = this.extractSubjectValue(row);
920
934
  if (typeof subjectValue === 'string' && subjectValue.length > 0) {
921
935
  if (row['@id'] === undefined) {
922
936
  row['@id'] = subjectValue;
@@ -946,7 +960,7 @@ class SelectQueryBuilder {
946
960
  }
947
961
  result[`${alias}.${key}`] = value;
948
962
  }
949
- const subjectValue = row.subject;
963
+ const subjectValue = this.extractSubjectValue(row);
950
964
  if (subjectValue) {
951
965
  result.subject = subjectValue;
952
966
  result['@id'] = subjectValue;
@@ -1234,7 +1248,17 @@ class SelectQueryBuilder {
1234
1248
  return;
1235
1249
  }
1236
1250
  if (!merged.has(key)) {
1237
- merged.set(key, { ...row });
1251
+ const normalizedRow = { ...row };
1252
+ Object.entries(normalizedRow).forEach(([col, value]) => {
1253
+ if (value === undefined)
1254
+ return;
1255
+ const colDef = this.getColumnDefinitionForRowKey(col);
1256
+ const isArrayType = colDef?.options?.isArray || colDef?.dataType === 'array';
1257
+ if (isArrayType && !Array.isArray(value)) {
1258
+ normalizedRow[col] = [value];
1259
+ }
1260
+ });
1261
+ merged.set(key, normalizedRow);
1238
1262
  order.push(key);
1239
1263
  return;
1240
1264
  }
@@ -1274,7 +1298,7 @@ class SelectQueryBuilder {
1274
1298
  combined.push(entry);
1275
1299
  }
1276
1300
  });
1277
- const colDef = this.selectedTable?.columns[col];
1301
+ const colDef = this.getColumnDefinitionForRowKey(col);
1278
1302
  const isArrayType = colDef?.options?.isArray || colDef?.dataType === 'array';
1279
1303
  if (!isArrayType && combined.length > 1) {
1280
1304
  console.warn(`[Data Integrity] Multiple values found for single-value column '${col}' on subject '${key}'. Using first value.`);
@@ -1294,6 +1318,22 @@ class SelectQueryBuilder {
1294
1318
  const result = order.map((key) => merged.get(key));
1295
1319
  return result;
1296
1320
  }
1321
+ getColumnDefinitionForRowKey(key) {
1322
+ if (!key) {
1323
+ return undefined;
1324
+ }
1325
+ if (!key.includes('.')) {
1326
+ return this.selectedTable?.columns[key];
1327
+ }
1328
+ const [alias, columnName] = key.split('.', 2);
1329
+ if (!alias || !columnName) {
1330
+ return undefined;
1331
+ }
1332
+ if (alias === this.primaryAlias) {
1333
+ return this.selectedTable?.columns[columnName];
1334
+ }
1335
+ return this.aliasToTable.get(alias)?.columns?.[columnName];
1336
+ }
1297
1337
  inferSourceFromChild(childIri, table) {
1298
1338
  if (!childIri) {
1299
1339
  return this.session.getDialect().getPodUrl();
@@ -1357,21 +1397,154 @@ class SelectQueryBuilder {
1357
1397
  return [];
1358
1398
  }
1359
1399
  const valuesArray = Array.from(uniqueValues.values());
1400
+ const buildJoinQuery = () => this.session.select().from(join.table);
1360
1401
  const joinColumnInstance = join.table.getColumn(joinRef.column);
1361
- let joinQuery = this.session.select().from(join.table);
1362
- if (joinColumnInstance && joinRef.column !== 'id') {
1363
- joinQuery = joinQuery.where((0, query_conditions_1.inArray)(joinColumnInstance, valuesArray));
1402
+ const exactJoinConditions = this.buildExactJoinLookupConditions(join, baseRows);
1403
+ if (exactJoinConditions && exactJoinConditions.length > 0) {
1404
+ const exactJoinRows = [];
1405
+ for (const joinCondition of exactJoinConditions) {
1406
+ const joinQuery = buildJoinQuery();
1407
+ const conditionedJoinQuery = (0, query_where_policy_1.conditionTargetsReservedIdentifier)(joinCondition)
1408
+ ? joinQuery.applyInternalQueryCondition(joinCondition)
1409
+ : joinQuery.where(joinCondition);
1410
+ const joinRows = await conditionedJoinQuery;
1411
+ exactJoinRows.push(...joinRows);
1412
+ }
1413
+ return this.mergeRowsBySubject(this.normalizeJoinRows(join, exactJoinRows));
1414
+ }
1415
+ const joinLookupCondition = this.buildJoinLookupCondition(joinColumnInstance, valuesArray);
1416
+ if (joinLookupCondition) {
1417
+ const joinQuery = buildJoinQuery();
1418
+ const filteredJoinQuery = (0, query_where_policy_1.conditionTargetsReservedIdentifier)(joinLookupCondition)
1419
+ ? joinQuery.applyInternalQueryCondition(joinLookupCondition)
1420
+ : joinQuery.where(joinLookupCondition);
1421
+ const joinRows = await filteredJoinQuery;
1422
+ return this.normalizeJoinRows(join, joinRows);
1364
1423
  }
1365
- const joinRows = await joinQuery;
1424
+ const joinRows = await buildJoinQuery();
1366
1425
  return this.normalizeJoinRows(join, joinRows);
1367
1426
  }
1427
+ buildExactJoinLookupConditions(join, baseRows) {
1428
+ const conditions = join.resolvedConditions ?? [];
1429
+ if (conditions.length === 0) {
1430
+ return undefined;
1431
+ }
1432
+ const [primaryCondition] = conditions;
1433
+ const primaryJoinRef = primaryCondition.left.alias === join.alias ? primaryCondition.left : primaryCondition.right;
1434
+ const primaryBaseRef = primaryJoinRef === primaryCondition.left ? primaryCondition.right : primaryCondition.left;
1435
+ const requiredLocatorVars = this.getRequiredSubjectTemplateVariables(join.table);
1436
+ const joinUsesTemplateScopedId = primaryJoinRef.column === 'id' && requiredLocatorVars.length > 0;
1437
+ if (!joinUsesTemplateScopedId && conditions.length === 1) {
1438
+ return undefined;
1439
+ }
1440
+ const locatorConditionMap = new Map();
1441
+ for (const condition of conditions) {
1442
+ const joinRef = condition.left.alias === join.alias ? condition.left : condition.right;
1443
+ locatorConditionMap.set(joinRef.column, condition);
1444
+ }
1445
+ const exactConditions = [];
1446
+ const seen = new Set();
1447
+ for (const baseRow of baseRows) {
1448
+ const primaryValue = this.getRowValueForColumn(baseRow, primaryBaseRef);
1449
+ if (primaryValue === undefined || primaryValue === null) {
1450
+ continue;
1451
+ }
1452
+ const primaryIsAbsoluteIri = typeof primaryValue === 'string' && this.isAbsoluteIri(primaryValue);
1453
+ const clauses = [];
1454
+ const dedupeParts = [];
1455
+ let skipRow = false;
1456
+ for (const condition of conditions) {
1457
+ const joinRef = condition.left.alias === join.alias ? condition.left : condition.right;
1458
+ const baseRef = joinRef === condition.left ? condition.right : condition.left;
1459
+ const baseValue = this.getRowValueForColumn(baseRow, baseRef);
1460
+ if (baseValue === undefined || baseValue === null) {
1461
+ if (joinUsesTemplateScopedId && !primaryIsAbsoluteIri && requiredLocatorVars.includes(joinRef.column)) {
1462
+ throw new Error(this.buildMissingJoinLocatorError(join, requiredLocatorVars.filter((variable) => {
1463
+ const variableCondition = locatorConditionMap.get(variable);
1464
+ if (!variableCondition) {
1465
+ return true;
1466
+ }
1467
+ const variableJoinRef = variableCondition.left.alias === join.alias ? variableCondition.left : variableCondition.right;
1468
+ const variableBaseRef = variableJoinRef === variableCondition.left ? variableCondition.right : variableCondition.left;
1469
+ const variableValue = this.getRowValueForColumn(baseRow, variableBaseRef);
1470
+ return variableValue === undefined || variableValue === null;
1471
+ })));
1472
+ }
1473
+ skipRow = true;
1474
+ break;
1475
+ }
1476
+ const joinColumn = join.table.getColumn(joinRef.column);
1477
+ if (!joinColumn) {
1478
+ skipRow = true;
1479
+ break;
1480
+ }
1481
+ clauses.push((0, query_conditions_1.eq)(joinColumn, baseValue));
1482
+ dedupeParts.push(`${joinRef.column}:${this.serializeValueForKey(baseValue)}`);
1483
+ }
1484
+ if (skipRow) {
1485
+ continue;
1486
+ }
1487
+ if (joinUsesTemplateScopedId && !primaryIsAbsoluteIri) {
1488
+ const missingLocatorVars = requiredLocatorVars.filter((variable) => {
1489
+ const variableCondition = locatorConditionMap.get(variable);
1490
+ if (!variableCondition) {
1491
+ return true;
1492
+ }
1493
+ const variableJoinRef = variableCondition.left.alias === join.alias ? variableCondition.left : variableCondition.right;
1494
+ const variableBaseRef = variableJoinRef === variableCondition.left ? variableCondition.right : variableCondition.left;
1495
+ const variableValue = this.getRowValueForColumn(baseRow, variableBaseRef);
1496
+ return variableValue === undefined || variableValue === null;
1497
+ });
1498
+ if (missingLocatorVars.length > 0) {
1499
+ throw new Error(this.buildMissingJoinLocatorError(join, missingLocatorVars));
1500
+ }
1501
+ }
1502
+ if (clauses.length === 0) {
1503
+ continue;
1504
+ }
1505
+ const dedupeKey = dedupeParts.join('|');
1506
+ if (seen.has(dedupeKey)) {
1507
+ continue;
1508
+ }
1509
+ seen.add(dedupeKey);
1510
+ exactConditions.push(clauses.length === 1 ? clauses[0] : (0, query_conditions_1.and)(...clauses));
1511
+ }
1512
+ return exactConditions.length > 0 ? exactConditions : undefined;
1513
+ }
1514
+ buildJoinLookupCondition(joinColumnInstance, valuesArray) {
1515
+ if (!joinColumnInstance || valuesArray.length === 0) {
1516
+ return undefined;
1517
+ }
1518
+ if (valuesArray.length === 1) {
1519
+ return (0, query_conditions_1.eq)(joinColumnInstance, valuesArray[0]);
1520
+ }
1521
+ return (0, query_conditions_1.inArray)(joinColumnInstance, valuesArray);
1522
+ }
1523
+ getRequiredSubjectTemplateVariables(table) {
1524
+ const template = table.getSubjectTemplate?.() ?? table.config?.subjectTemplate ?? '';
1525
+ return Array.from(template.matchAll(/\{([^}]+)\}/g))
1526
+ .map((match) => match[1])
1527
+ .filter((variable) => variable !== 'id' && variable !== 'index');
1528
+ }
1529
+ isAbsoluteIri(value) {
1530
+ return /^[a-zA-Z][\w+.-]*:\/\//.test(value);
1531
+ }
1532
+ buildMissingJoinLocatorError(join, missingVariables) {
1533
+ const template = join.table.getSubjectTemplate?.() ?? join.table.config?.subjectTemplate ?? '{id}';
1534
+ const uniqueMissingVariables = Array.from(new Set(missingVariables));
1535
+ const exampleVariable = uniqueMissingVariables[0] ?? '...';
1536
+ return (`Cannot join table "${join.table.config.name ?? 'unknown'}" by id with subjectTemplate "${template}" ` +
1537
+ `because locator variable(s) [${uniqueMissingVariables.join(', ')}] are missing. ` +
1538
+ `Add join conditions for all required template variables (for example eq(base.${exampleVariable}, join.${exampleVariable})) ` +
1539
+ `or join via full IRI values.`);
1540
+ }
1368
1541
  normalizeJoinRows(join, rows) {
1369
1542
  return rows.map((row) => {
1370
1543
  const normalized = {};
1371
1544
  for (const [key, value] of Object.entries(row)) {
1372
1545
  normalized[`${join.alias}.${key}`] = value;
1373
1546
  }
1374
- const subjectValue = row.subject;
1547
+ const subjectValue = this.extractSubjectValue(row);
1375
1548
  if (subjectValue) {
1376
1549
  normalized.subject = subjectValue;
1377
1550
  normalized['@id'] = subjectValue;
@@ -1423,7 +1596,8 @@ class SelectQueryBuilder {
1423
1596
  for (const baseRow of baseRows) {
1424
1597
  const baseValue = this.getRowValueForColumn(baseRow, baseRef);
1425
1598
  const key = this.serializeValueForKey(baseValue);
1426
- const matches = baseValue !== undefined && baseValue !== null ? joinValueMap.get(key) ?? [] : [];
1599
+ const primaryMatches = baseValue !== undefined && baseValue !== null ? joinValueMap.get(key) ?? [] : [];
1600
+ const matches = primaryMatches.filter((joinRow) => this.joinRowMatchesAllConditions(baseRow, joinRow, join.alias, conditions));
1427
1601
  if (matches.length === 0) {
1428
1602
  if (join.type === 'innerJoin') {
1429
1603
  continue;
@@ -1437,6 +1611,18 @@ class SelectQueryBuilder {
1437
1611
  }
1438
1612
  return merged;
1439
1613
  }
1614
+ joinRowMatchesAllConditions(baseRow, joinRow, joinAlias, conditions) {
1615
+ return conditions.every((condition) => {
1616
+ const joinRef = condition.left.alias === joinAlias ? condition.left : condition.right;
1617
+ const baseRef = joinRef === condition.left ? condition.right : condition.left;
1618
+ const baseValue = this.getRowValueForColumn(baseRow, baseRef);
1619
+ const joinValue = this.getRowValueForColumn(joinRow, joinRef);
1620
+ if (baseValue === undefined || baseValue === null || joinValue === undefined || joinValue === null) {
1621
+ return false;
1622
+ }
1623
+ return this.serializeValueForKey(baseValue) === this.serializeValueForKey(joinValue);
1624
+ });
1625
+ }
1440
1626
  createEmptyJoinRow(join) {
1441
1627
  const empty = {};
1442
1628
  for (const columnName of Object.keys(join.table.columns)) {