@memberjunction/core 5.51.0 → 6.1.0-edge.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/dist/generic/baseEngine.d.ts.map +1 -1
- package/dist/generic/baseEngine.js +13 -2
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.d.ts +14 -0
- package/dist/generic/baseEngineRegistry.d.ts.map +1 -1
- package/dist/generic/baseEngineRegistry.js +32 -0
- package/dist/generic/baseEngineRegistry.js.map +1 -1
- package/dist/generic/baseEntity.d.ts +329 -26
- package/dist/generic/baseEntity.d.ts.map +1 -1
- package/dist/generic/baseEntity.js +788 -79
- package/dist/generic/baseEntity.js.map +1 -1
- package/dist/generic/databaseProviderBase.d.ts +54 -17
- package/dist/generic/databaseProviderBase.d.ts.map +1 -1
- package/dist/generic/databaseProviderBase.js +133 -52
- package/dist/generic/databaseProviderBase.js.map +1 -1
- package/dist/generic/entityCompanion.d.ts +218 -0
- package/dist/generic/entityCompanion.d.ts.map +1 -0
- package/dist/generic/entityCompanion.js +170 -0
- package/dist/generic/entityCompanion.js.map +1 -0
- package/dist/generic/entityInfo.d.ts +146 -0
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +188 -0
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/entitySavePlan.d.ts +199 -0
- package/dist/generic/entitySavePlan.d.ts.map +1 -0
- package/dist/generic/entitySavePlan.js +213 -0
- package/dist/generic/entitySavePlan.js.map +1 -0
- package/dist/generic/entityTransactionScope.d.ts +125 -0
- package/dist/generic/entityTransactionScope.d.ts.map +1 -0
- package/dist/generic/entityTransactionScope.js +115 -0
- package/dist/generic/entityTransactionScope.js.map +1 -0
- package/dist/generic/interfaces.d.ts +93 -35
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js +27 -0
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/providerBase.d.ts +13 -0
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +64 -5
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/relatedRecordBatchLoader.d.ts +39 -0
- package/dist/generic/relatedRecordBatchLoader.d.ts.map +1 -0
- package/dist/generic/relatedRecordBatchLoader.js +154 -0
- package/dist/generic/relatedRecordBatchLoader.js.map +1 -0
- package/dist/generic/relatedRecordCollection.d.ts +578 -0
- package/dist/generic/relatedRecordCollection.d.ts.map +1 -0
- package/dist/generic/relatedRecordCollection.js +1004 -0
- package/dist/generic/relatedRecordCollection.js.map +1 -0
- package/dist/generic/saveEntityGraphOperation.d.ts +148 -0
- package/dist/generic/saveEntityGraphOperation.d.ts.map +1 -0
- package/dist/generic/saveEntityGraphOperation.js +157 -0
- package/dist/generic/saveEntityGraphOperation.js.map +1 -0
- package/dist/generic/securityInfo.d.ts +99 -1
- package/dist/generic/securityInfo.d.ts.map +1 -1
- package/dist/generic/securityInfo.js +88 -6
- package/dist/generic/securityInfo.js.map +1 -1
- package/dist/generic/telemetryManager.d.ts +21 -1
- package/dist/generic/telemetryManager.d.ts.map +1 -1
- package/dist/generic/telemetryManager.js +21 -6
- package/dist/generic/telemetryManager.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +31 -0
- package/dist/views/runView.d.ts.map +1 -1
- package/dist/views/runView.js.map +1 -1
- package/package.json +13 -13
- package/readme.md +159 -1
- package/dist/generic/runReport.d.ts +0 -25
- package/dist/generic/runReport.d.ts.map +0 -1
- package/dist/generic/runReport.js +0 -38
- package/dist/generic/runReport.js.map +0 -1
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Batched child-collection loading for result sets.
|
|
3
|
+
*
|
|
4
|
+
* Populates a named {@link RelatedRecordCollection} across every row of a `RunView` result using **one**
|
|
5
|
+
* query per collection, rather than one per row.
|
|
6
|
+
*
|
|
7
|
+
* ## The N+1 this exists to prevent
|
|
8
|
+
*
|
|
9
|
+
* The intuitive way to make children load automatically is to do it in `BaseEntity.LoadFromData()`.
|
|
10
|
+
* That method is also how every row of `RunView(ResultType:'entity_object')` is materialised, so the
|
|
11
|
+
* intuitive implementation silently multiplies a single view into one child query per row. This is
|
|
12
|
+
* not theoretical: `JournalEntryEntityServer.LoadFromData()` overrides the method to call
|
|
13
|
+
* `LoadLines()`, so listing 500 journal entries issues 500 line queries — plus another 500 for line
|
|
14
|
+
* dimensions.
|
|
15
|
+
*
|
|
16
|
+
* Companion eager loading is therefore excluded from `LoadFromData()` by design, and set-oriented
|
|
17
|
+
* loading goes through here: gather every parent key, issue one `WHERE fk IN (...)`, bucket the rows
|
|
18
|
+
* by parent, and hand each parent its own slice.
|
|
19
|
+
*
|
|
20
|
+
* @module @memberjunction/core
|
|
21
|
+
*/
|
|
22
|
+
import { RelatedRecordCollection } from './relatedRecordCollection.js';
|
|
23
|
+
import { LogError } from './logging.js';
|
|
24
|
+
/**
|
|
25
|
+
* Populates the named child collections across a set of already-materialised parent entities.
|
|
26
|
+
*
|
|
27
|
+
* Unknown collection names are logged and skipped rather than thrown, so a caller asking for a
|
|
28
|
+
* collection that a particular subclass does not declare degrades to "no children loaded" instead of
|
|
29
|
+
* failing the whole view.
|
|
30
|
+
*
|
|
31
|
+
* @param parents - The entity objects returned by the view. Entities that do not declare a named
|
|
32
|
+
* collection are left untouched.
|
|
33
|
+
* @param collectionNames - The companion names to populate.
|
|
34
|
+
* @param provider - The provider used to issue the batched queries.
|
|
35
|
+
* @param contextUser - The acting user, required server-side.
|
|
36
|
+
*/
|
|
37
|
+
export async function LoadRelatedRecordsBatched(parents, collectionNames, provider, contextUser) {
|
|
38
|
+
if (!parents?.length || !collectionNames?.length) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
for (const name of collectionNames) {
|
|
42
|
+
await loadOneCollectionBatched(parents, name, provider, contextUser);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Populates a single named collection across all parents with one query.
|
|
47
|
+
*
|
|
48
|
+
* @param parents - The parent entities.
|
|
49
|
+
* @param collectionName - The companion name to populate.
|
|
50
|
+
* @param provider - The provider used to issue the query.
|
|
51
|
+
* @param contextUser - The acting user.
|
|
52
|
+
*/
|
|
53
|
+
async function loadOneCollectionBatched(parents, collectionName, provider, contextUser) {
|
|
54
|
+
// Use the first parent that actually declares the collection to read its configuration. Parents
|
|
55
|
+
// in one result set are the same entity, but a mixed set (or a subclass that conditionally
|
|
56
|
+
// declares) should not take the whole load down.
|
|
57
|
+
const template = parents
|
|
58
|
+
.map(p => p.GetCompanion(collectionName))
|
|
59
|
+
.find((c) => c instanceof RelatedRecordCollection);
|
|
60
|
+
if (!template) {
|
|
61
|
+
LogError(`RunView.IncludeRelatedRecords: no RelatedRecordCollection named '${collectionName}' is declared on ` +
|
|
62
|
+
`${parents[0]?.EntityInfo?.Name ?? 'the returned entity'}; skipping.`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const foreignKey = template.RelatedEntityJoinField;
|
|
66
|
+
const parentKeys = collectParentKeys(parents);
|
|
67
|
+
if (parentKeys.length === 0) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const result = await provider.RunView({
|
|
71
|
+
EntityName: template.RelatedEntityName,
|
|
72
|
+
ExtraFilter: `${foreignKey} IN (${parentKeys.map(k => `'${escapeSQLLiteral(k)}'`).join(',')})`,
|
|
73
|
+
OrderBy: template.OrderByClause,
|
|
74
|
+
ResultType: 'entity_object',
|
|
75
|
+
}, contextUser);
|
|
76
|
+
if (!result.Success) {
|
|
77
|
+
// Loud: quietly handing back empty collections would make populated parents look childless,
|
|
78
|
+
// and anything derived from that — a total, a reversal, a validation decision — is then
|
|
79
|
+
// wrong in a way nothing downstream can detect.
|
|
80
|
+
throw new Error(`RunView.IncludeRelatedRecords: failed to batch-load '${collectionName}' ` +
|
|
81
|
+
`(${template.RelatedEntityName}): ${result.ErrorMessage ?? 'unknown error'}`);
|
|
82
|
+
}
|
|
83
|
+
distributeChildren(parents, collectionName, foreignKey, result.Results ?? []);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Buckets loaded children by their foreign key and hands each parent its own slice.
|
|
87
|
+
*
|
|
88
|
+
* Every parent that declares the collection is assigned a slice — including an empty one. Assigning
|
|
89
|
+
* empty matters: it marks the collection as loaded, so a later `Items` read is known-empty rather
|
|
90
|
+
* than not-yet-loaded.
|
|
91
|
+
*
|
|
92
|
+
* @param parents - The parent entities.
|
|
93
|
+
* @param collectionName - The companion name being populated.
|
|
94
|
+
* @param foreignKey - The child field pointing back at the parent.
|
|
95
|
+
* @param children - All loaded children across every parent.
|
|
96
|
+
*/
|
|
97
|
+
function distributeChildren(parents, collectionName, foreignKey, children) {
|
|
98
|
+
const byParent = new Map();
|
|
99
|
+
for (const child of children) {
|
|
100
|
+
// Case-insensitive keying: MJ primary keys are UUIDs, and their casing is not guaranteed to
|
|
101
|
+
// survive a round trip identically on every platform.
|
|
102
|
+
const key = normalizeKey(child.Get(foreignKey));
|
|
103
|
+
const bucket = byParent.get(key);
|
|
104
|
+
if (bucket) {
|
|
105
|
+
bucket.push(child);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
byParent.set(key, [child]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
for (const parent of parents) {
|
|
112
|
+
const collection = parent.GetCompanion(collectionName);
|
|
113
|
+
if (!(collection instanceof RelatedRecordCollection)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const key = normalizeKey(parent.FirstPrimaryKey?.Value);
|
|
117
|
+
collection.SetLoadedItems(byParent.get(key) ?? []);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Collects the distinct primary-key values of the parents, skipping unsaved ones.
|
|
122
|
+
*
|
|
123
|
+
* @param parents - The parent entities.
|
|
124
|
+
* @returns Distinct key values as strings.
|
|
125
|
+
*/
|
|
126
|
+
function collectParentKeys(parents) {
|
|
127
|
+
const keys = new Set();
|
|
128
|
+
for (const parent of parents) {
|
|
129
|
+
const value = parent.FirstPrimaryKey?.Value;
|
|
130
|
+
if (value !== null && value !== undefined && value !== '') {
|
|
131
|
+
keys.add(String(value));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return Array.from(keys);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Normalises a key value for map lookups — string form, lower-cased, trimmed.
|
|
138
|
+
*
|
|
139
|
+
* @param value - The raw key value.
|
|
140
|
+
* @returns The normalised key.
|
|
141
|
+
*/
|
|
142
|
+
function normalizeKey(value) {
|
|
143
|
+
return String(value ?? '').trim().toLowerCase();
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Escapes a value for safe inclusion in a single-quoted SQL literal.
|
|
147
|
+
*
|
|
148
|
+
* @param value - The raw value.
|
|
149
|
+
* @returns The escaped value.
|
|
150
|
+
*/
|
|
151
|
+
function escapeSQLLiteral(value) {
|
|
152
|
+
return value.replace(/'/g, "''");
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=relatedRecordBatchLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relatedRecordBatchLoader.js","sourceRoot":"","sources":["../../src/generic/relatedRecordBatchLoader.ts"],"names":[],"mappings":"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"}
|