@nshiab/simple-data-analysis-core 2.1.2 → 2.1.3

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 (35) hide show
  1. package/esm/class/SimpleDB.d.ts.map +1 -1
  2. package/esm/class/SimpleDB.js +23 -14
  3. package/esm/class/SimpleTable.d.ts +18 -6
  4. package/esm/class/SimpleTable.d.ts.map +1 -1
  5. package/esm/class/SimpleTable.js +20 -8
  6. package/esm/helpers/cleanCache.d.ts +1 -1
  7. package/esm/helpers/cleanCache.d.ts.map +1 -1
  8. package/esm/helpers/cleanCache.js +2 -2
  9. package/esm/helpers/updateColumnsWithJS.d.ts.map +1 -1
  10. package/esm/helpers/updateColumnsWithJS.js +14 -12
  11. package/esm/methods/similarityMahalanobis.d.ts +5 -0
  12. package/esm/methods/similarityMahalanobis.d.ts.map +1 -0
  13. package/esm/methods/{mahalanobis.js → similarityMahalanobis.js} +9 -9
  14. package/esm/methods/updateWithJS.js +24 -5
  15. package/llm.md +19 -10
  16. package/llms.txt +1 -1
  17. package/package.json +2 -2
  18. package/script/class/SimpleDB.d.ts.map +1 -1
  19. package/script/class/SimpleDB.js +23 -14
  20. package/script/class/SimpleTable.d.ts +18 -6
  21. package/script/class/SimpleTable.d.ts.map +1 -1
  22. package/script/class/SimpleTable.js +20 -8
  23. package/script/helpers/cleanCache.d.ts +1 -1
  24. package/script/helpers/cleanCache.d.ts.map +1 -1
  25. package/script/helpers/cleanCache.js +2 -2
  26. package/script/helpers/updateColumnsWithJS.d.ts.map +1 -1
  27. package/script/helpers/updateColumnsWithJS.js +13 -11
  28. package/script/methods/similarityMahalanobis.d.ts +5 -0
  29. package/script/methods/similarityMahalanobis.d.ts.map +1 -0
  30. package/script/methods/{mahalanobis.js → similarityMahalanobis.js} +10 -10
  31. package/script/methods/updateWithJS.js +24 -5
  32. package/esm/methods/mahalanobis.d.ts +0 -5
  33. package/esm/methods/mahalanobis.d.ts.map +0 -1
  34. package/script/methods/mahalanobis.d.ts +0 -5
  35. package/script/methods/mahalanobis.d.ts.map +0 -1
@@ -8,6 +8,8 @@ const quoteIdentifier_js_1 = __importDefault(require("./quoteIdentifier.js"));
8
8
  const readMutationRows_js_1 = __importDefault(require("./readMutationRows.js"));
9
9
  const tableRegistry_js_1 = require("./tableRegistry.js");
10
10
  const loadArray_js_1 = require("../methods/loadArray.js");
11
+ const appendColumnBatches_js_1 = __importDefault(require("./appendColumnBatches.js"));
12
+ const parseDuckDBType_js_1 = __importDefault(require("./parseDuckDBType.js"));
11
13
  /**
12
14
  * Generates columns from selected input columns, keeping other SQL values in
13
15
  * DuckDB. Call from an asynchronous extension barrier. The callback must return
@@ -74,7 +76,6 @@ async function updateColumnsWithJS(table, inputColumns, outputColumns, generate,
74
76
  const id = `__sda_id_${suffix}`;
75
77
  const snapshot = `__sda_source_${suffix}`;
76
78
  const staged = table.sdb.newTable(`__sda_generated_${suffix}`);
77
- const batch = table.sdb.newTable(`__sda_batch_${suffix}`);
78
79
  const q = quoteIdentifier_js_1.default;
79
80
  try {
80
81
  // A SQL snapshot gives every row a stable identity, including duplicate rows
@@ -98,17 +99,17 @@ async function updateColumnsWithJS(table, inputColumns, outputColumns, generate,
98
99
  throw new Error("Column generation must return one row per input row.");
99
100
  }
100
101
  // Generated vectors retain this helper's existing inference contract.
101
- await (0, loadArray_js_1.executePreparedArray)(batch, (0, loadArray_js_1.prepareArray)(generated.map((row, i) => ({
102
+ const prepared = (0, loadArray_js_1.prepareArray)(generated.map((row, i) => ({
102
103
  ...Object.fromEntries(outputColumns.map((name) => [name, row[name]])),
103
104
  [id]: ids[i],
104
- }))));
105
- const batchTypes = await batch.getTypes();
105
+ })));
106
+ const batchTypes = new Map(prepared.keys.map((name, i) => [name, prepared.types[i]]));
106
107
  for (const name of outputColumns) {
107
108
  const nonNull = generated.some((row) => row[name] !== null && row[name] !== undefined);
108
109
  if (!nonNull)
109
110
  continue;
110
111
  const previous = knownTypes.get(name);
111
- const current = batchTypes[name];
112
+ const current = batchTypes.get(name);
112
113
  if (previous !== undefined && previous !== current) {
113
114
  throw new Error(`Generated column ${JSON.stringify(name)} changed type from ${previous} to ${current}.`);
114
115
  }
@@ -120,12 +121,13 @@ async function updateColumnsWithJS(table, inputColumns, outputColumns, generate,
120
121
  }
121
122
  }
122
123
  if (!hasRows) {
123
- await table.sdb.customQuery(`CREATE TEMP TABLE ${q(staged.name)} AS SELECT * FROM ${q(batch.name)}`);
124
+ await table.sdb.customQuery(`CREATE TEMP TABLE ${q(staged.name)} (${prepared.keys.map((name, i) => `${q(name)} ${prepared.types[i]}`)
125
+ .join(", ")})`);
124
126
  hasRows = true;
125
127
  }
126
- else {
127
- await table.sdb.customQuery(`INSERT INTO ${q(staged.name)} SELECT * FROM ${q(batch.name)}`);
128
- }
128
+ // All-null batches use the column's established type, avoiding a separate
129
+ // batch table and SQL copy just to cast nulls into the staged schema.
130
+ await (0, appendColumnBatches_js_1.default)(table.connection, staged.name, prepared.keys.map((name, i) => (0, parseDuckDBType_js_1.default)(knownTypes.get(name) ?? prepared.types[i])), prepared.rowCount, (column, start, end) => prepared.columnsData[column].slice(start, end));
129
131
  }
130
132
  if (!hasRows)
131
133
  return;
@@ -145,7 +147,7 @@ async function updateColumnsWithJS(table, inputColumns, outputColumns, generate,
145
147
  await table.sdb.customQuery(`CREATE OR REPLACE TABLE ${q(table.name)} AS SELECT ${columns.join(", ")} FROM ${q(snapshot)} original JOIN ${q(staged.name)} generated ON original.${q(id)} = CAST(generated.${q(id)} AS BIGINT) ORDER BY original.${q(id)}`);
146
148
  }
147
149
  finally {
148
- await table.sdb.customQuery(`DROP TABLE IF EXISTS ${q(snapshot)}; DROP TABLE IF EXISTS ${q(staged.name)}; DROP TABLE IF EXISTS ${q(batch.name)};`);
149
- (0, tableRegistry_js_1.retainRegisteredTables)(table.sdb, (item) => item !== staged && item !== batch);
150
+ await table.sdb.customQuery(`DROP TABLE IF EXISTS ${q(snapshot)}; DROP TABLE IF EXISTS ${q(staged.name)};`);
151
+ (0, tableRegistry_js_1.retainRegisteredTables)(table.sdb, (item) => item !== staged);
150
152
  }
151
153
  }
@@ -0,0 +1,5 @@
1
+ import type SimpleTable from "../class/SimpleTable.js";
2
+ type Options = NonNullable<Parameters<SimpleTable["similarityMahalanobis"]>[3]>;
3
+ export default function similarityMahalanobis(table: SimpleTable, columns: string | string[], referencePoint: number[], newColumn: string, options?: Options): void;
4
+ export {};
5
+ //# sourceMappingURL=similarityMahalanobis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"similarityMahalanobis.d.ts","sourceRoot":"","sources":["../../src/methods/similarityMahalanobis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,KAAK,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhF,MAAM,CAAC,OAAO,UAAU,qBAAqB,CAC3C,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,OAAY,GACpB,IAAI,CAyBN"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.default = mahalanobis;
6
+ exports.default = similarityMahalanobis;
7
7
  const computeCovariance_js_1 = __importDefault(require("../helpers/computeCovariance.js"));
8
8
  const computeMahalanobisDistances_js_1 = __importDefault(require("../helpers/computeMahalanobisDistances.js"));
9
9
  const foldIdentifier_js_1 = __importDefault(require("../helpers/foldIdentifier.js"));
@@ -11,17 +11,17 @@ const prepareNumericFeatures_js_1 = __importDefault(require("../helpers/prepareN
11
11
  const publishPreparedColumns_js_1 = __importDefault(require("../helpers/publishPreparedColumns.js"));
12
12
  const queueOp_js_1 = __importDefault(require("../helpers/queueOp.js"));
13
13
  const quoteIdentifier_js_1 = __importDefault(require("../helpers/quoteIdentifier.js"));
14
- function mahalanobis(table, columns, referencePoint, newColumn, options = {}) {
14
+ function similarityMahalanobis(table, columns, referencePoint, newColumn, options = {}) {
15
15
  if (!Array.isArray(referencePoint) || referencePoint.length === 0 ||
16
16
  Array.from(referencePoint).some((value) => typeof value !== "number" || !Number.isFinite(value))) {
17
- throw new Error("mahalanobis() referencePoint must be a nonempty array of finite numbers in feature-dimension order.");
17
+ throw new Error("similarityMahalanobis() referencePoint must be a nonempty array of finite numbers in feature-dimension order.");
18
18
  }
19
19
  const selected = typeof columns === "string" ? columns : [...columns];
20
20
  const reference = [...referencePoint];
21
21
  const settings = { ...options };
22
22
  (0, queueOp_js_1.default)(table, {
23
23
  kind: "barrier",
24
- method: "mahalanobis()",
24
+ method: "similarityMahalanobis()",
25
25
  parameters: {
26
26
  columns: selected,
27
27
  referencePoint: reference,
@@ -39,23 +39,23 @@ async function execute(table, columns, referencePoint, newColumn, options) {
39
39
  const seen = new Set();
40
40
  for (const name of outputNames) {
41
41
  if (typeof name !== "string" || name.length === 0 || name.includes("\0")) {
42
- throw new Error("mahalanobis() output column names must be nonempty strings without null characters.");
42
+ throw new Error("similarityMahalanobis() output column names must be nonempty strings without null characters.");
43
43
  }
44
44
  const folded = (0, foldIdentifier_js_1.default)(name);
45
45
  if (seen.has(folded)) {
46
- throw new Error("mahalanobis() distance and similarity score columns must have different names (case-insensitive).");
46
+ throw new Error("similarityMahalanobis() distance and similarity score columns must have different names (case-insensitive).");
47
47
  }
48
48
  seen.add(folded);
49
49
  if (sourceColumns.some((column) => (0, foldIdentifier_js_1.default)(column) === folded)) {
50
- throw new Error(`mahalanobis() cannot create ${(0, quoteIdentifier_js_1.default)(name)} because that column already exists. Remove it first or choose a different name.`);
50
+ throw new Error(`similarityMahalanobis() cannot create ${(0, quoteIdentifier_js_1.default)(name)} because that column already exists. Remove it first or choose a different name.`);
51
51
  }
52
52
  }
53
53
  const prepared = await (0, prepareNumericFeatures_js_1.default)(table, typeof columns === "string"
54
54
  ? { kind: "vector", column: columns }
55
- : { kind: "scalars", columns }, { method: "mahalanobis()" });
55
+ : { kind: "scalars", columns }, { method: "similarityMahalanobis()" });
56
56
  try {
57
57
  if (referencePoint.length !== prepared.dimensions) {
58
- throw new Error(`mahalanobis() referencePoint must contain ${prepared.dimensions} values in feature-dimension order; received ${referencePoint.length}.`);
58
+ throw new Error(`similarityMahalanobis() referencePoint must contain ${prepared.dimensions} values in feature-dimension order; received ${referencePoint.length}.`);
59
59
  }
60
60
  const model = await (0, computeCovariance_js_1.default)(table.connection, {
61
61
  relation: prepared.relation,
@@ -79,7 +79,7 @@ async function execute(table, columns, referencePoint, newColumn, options) {
79
79
  });
80
80
  }
81
81
  await (0, publishPreparedColumns_js_1.default)(table, prepared, {
82
- method: "mahalanobis()",
82
+ method: "similarityMahalanobis()",
83
83
  parameters: { columns, referencePoint, newColumn, options },
84
84
  result: {
85
85
  relation: distances.relation,
@@ -66,6 +66,7 @@ async function executeUpdateWithJS(simpleTable, dataModifier, options) {
66
66
  const accumulator = `updateWithJS_accumulator_${suffix}`;
67
67
  try {
68
68
  let first = true;
69
+ let accumulatorSchema;
69
70
  let lastRowid = null;
70
71
  let sawRows = false;
71
72
  let outputOffset = 0;
@@ -137,9 +138,17 @@ async function executeUpdateWithJS(simpleTable, dataModifier, options) {
137
138
  ? (0, parseDuckDBType_js_1.default)("VARCHAR")
138
139
  : source.types.get(key) ??
139
140
  (0, parseDuckDBType_js_1.default)(added.types[newKeys.indexOf(key)]));
140
- await simpleTable.sdb.customQuery(`CREATE OR REPLACE TABLE ${(0, quoteIdentifier_js_1.default)(scratch.name)} (${keys.map((key, i) => `${(0, quoteIdentifier_js_1.default)(key)} ${columnTypes[i]}`)
141
- .join(", ")})`);
142
- await (0, appendColumnBatches_js_1.default)(simpleTable.connection, scratch.name, columnTypes, modified.length, (column, start, end) => {
141
+ const schema = JSON.stringify(keys.map((key, i) => [key, columnTypes[i].toString()]));
142
+ // Append matching native batches directly. SQL still handles conversion
143
+ // and BY NAME alignment when a callback changes the batch schema.
144
+ const direct = outputGeometry.length === 0 && outputJSON.length === 0 &&
145
+ (first || schema === accumulatorSchema);
146
+ const destination = direct ? accumulator : scratch.name;
147
+ if (!direct || first) {
148
+ await simpleTable.sdb.customQuery(`CREATE OR REPLACE TABLE ${(0, quoteIdentifier_js_1.default)(destination)} (${keys.map((key, i) => `${(0, quoteIdentifier_js_1.default)(key)} ${columnTypes[i]}`)
149
+ .join(", ")})`);
150
+ }
151
+ await (0, appendColumnBatches_js_1.default)(simpleTable.connection, destination, columnTypes, modified.length, (column, start, end) => {
143
152
  const key = keys[column];
144
153
  return outputGeometry.includes(key)
145
154
  ? geometryData.get(key).slice(start, end)
@@ -152,7 +161,11 @@ async function executeUpdateWithJS(simpleTable, dataModifier, options) {
152
161
  : outputJSON.includes(key)
153
162
  ? `${(0, quoteIdentifier_js_1.default)(key)}::JSON AS ${(0, quoteIdentifier_js_1.default)(key)}`
154
163
  : (0, quoteIdentifier_js_1.default)(key)).join(", ");
155
- if (first) {
164
+ if (direct) {
165
+ accumulatorSchema = schema;
166
+ first = false;
167
+ }
168
+ else if (first) {
156
169
  await simpleTable.sdb.customQuery(`CREATE OR REPLACE TABLE ${(0, quoteIdentifier_js_1.default)(accumulator)} AS SELECT ${projection} FROM ${(0, quoteIdentifier_js_1.default)(scratch.name)}`);
157
170
  first = false;
158
171
  }
@@ -165,7 +178,13 @@ async function executeUpdateWithJS(simpleTable, dataModifier, options) {
165
178
  if (first) {
166
179
  throw new Error("The dataModifier returned no rows. updateWithJS can't infer the table schema from zero rows.");
167
180
  }
168
- await simpleTable.sdb.customQuery(`CREATE OR REPLACE TABLE ${(0, quoteIdentifier_js_1.default)(simpleTable.name)} AS SELECT * FROM ${(0, quoteIdentifier_js_1.default)(accumulator)}`);
181
+ // Publish the completed ordinary table without copying its rows again.
182
+ // Qualify the creation namespace: a temporary table or a later search-path
183
+ // schema can shadow the name without being the destination of replacement.
184
+ const namespace = await simpleTable.connection.runAndReadAll("SELECT current_database(), current_schema()");
185
+ const prefix = namespace.getRows()[0].map((name) => (0, quoteIdentifier_js_1.default)(String(name))).join(".");
186
+ await simpleTable.sdb.customQuery(`DROP TABLE IF EXISTS ${prefix}.${(0, quoteIdentifier_js_1.default)(simpleTable.name)};
187
+ ALTER TABLE ${prefix}.${(0, quoteIdentifier_js_1.default)(accumulator)} RENAME TO ${(0, quoteIdentifier_js_1.default)(simpleTable.name)};`);
169
188
  }
170
189
  finally {
171
190
  await simpleTable.sdb.customQuery(`DROP TABLE IF EXISTS ${(0, quoteIdentifier_js_1.default)(accumulator)};
@@ -1,5 +0,0 @@
1
- import type SimpleTable from "../class/SimpleTable.js";
2
- type Options = NonNullable<Parameters<SimpleTable["mahalanobis"]>[3]>;
3
- export default function mahalanobis(table: SimpleTable, columns: string | string[], referencePoint: number[], newColumn: string, options?: Options): void;
4
- export {};
5
- //# sourceMappingURL=mahalanobis.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mahalanobis.d.ts","sourceRoot":"","sources":["../../src/methods/mahalanobis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,KAAK,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtE,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,OAAY,GACpB,IAAI,CAyBN"}
@@ -1,5 +0,0 @@
1
- import type SimpleTable from "../class/SimpleTable.js";
2
- type Options = NonNullable<Parameters<SimpleTable["mahalanobis"]>[3]>;
3
- export default function mahalanobis(table: SimpleTable, columns: string | string[], referencePoint: number[], newColumn: string, options?: Options): void;
4
- export {};
5
- //# sourceMappingURL=mahalanobis.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"mahalanobis.d.ts","sourceRoot":"","sources":["../../src/methods/mahalanobis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AASvD,KAAK,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAEtE,MAAM,CAAC,OAAO,UAAU,WAAW,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,cAAc,EAAE,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,OAAY,GACpB,IAAI,CAyBN"}