@nshiab/simple-data-analysis-core 0.0.19 → 0.0.21

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 (63) hide show
  1. package/esm/class/SimpleTable.d.ts +117 -22
  2. package/esm/class/SimpleTable.d.ts.map +1 -1
  3. package/esm/class/SimpleTable.js +172 -104
  4. package/esm/helpers/findGeoColumn.js +1 -1
  5. package/esm/helpers/getProjection.d.ts.map +1 -1
  6. package/esm/helpers/getProjection.js +3 -0
  7. package/esm/helpers/hasGeometryColumn.d.ts +20 -0
  8. package/esm/helpers/hasGeometryColumn.d.ts.map +1 -0
  9. package/esm/helpers/hasGeometryColumn.js +21 -0
  10. package/esm/helpers/writeGeoData.d.ts +9 -0
  11. package/esm/helpers/writeGeoData.d.ts.map +1 -0
  12. package/esm/helpers/writeGeoData.js +110 -0
  13. package/esm/methods/fill.d.ts.map +1 -1
  14. package/esm/methods/fill.js +16 -16
  15. package/esm/methods/fuzzyClean.d.ts +2 -2
  16. package/esm/methods/fuzzyClean.d.ts.map +1 -1
  17. package/esm/methods/fuzzyClean.js +25 -6
  18. package/esm/methods/fuzzyJoin.d.ts +2 -2
  19. package/esm/methods/fuzzyJoin.d.ts.map +1 -1
  20. package/esm/methods/fuzzyJoin.js +3 -3
  21. package/esm/methods/fuzzyJoinQuery.d.ts +1 -1
  22. package/esm/methods/fuzzyJoinQuery.d.ts.map +1 -1
  23. package/esm/methods/fuzzyJoinQuery.js +13 -3
  24. package/esm/methods/loadDataQuery.d.ts +1 -0
  25. package/esm/methods/loadDataQuery.d.ts.map +1 -1
  26. package/esm/methods/loadDataQuery.js +12 -4
  27. package/esm/methods/padQuery.d.ts +5 -0
  28. package/esm/methods/padQuery.d.ts.map +1 -0
  29. package/esm/methods/padQuery.js +14 -0
  30. package/esm/methods/writeGeoDataQuery.d.ts.map +1 -1
  31. package/esm/methods/writeGeoDataQuery.js +4 -1
  32. package/package.json +1 -1
  33. package/script/class/SimpleTable.d.ts +117 -22
  34. package/script/class/SimpleTable.d.ts.map +1 -1
  35. package/script/class/SimpleTable.js +172 -104
  36. package/script/helpers/findGeoColumn.js +1 -1
  37. package/script/helpers/getProjection.d.ts.map +1 -1
  38. package/script/helpers/getProjection.js +3 -0
  39. package/script/helpers/hasGeometryColumn.d.ts +20 -0
  40. package/script/helpers/hasGeometryColumn.d.ts.map +1 -0
  41. package/script/helpers/hasGeometryColumn.js +24 -0
  42. package/script/helpers/writeGeoData.d.ts +9 -0
  43. package/script/helpers/writeGeoData.d.ts.map +1 -0
  44. package/script/helpers/writeGeoData.js +116 -0
  45. package/script/methods/fill.d.ts.map +1 -1
  46. package/script/methods/fill.js +16 -16
  47. package/script/methods/fuzzyClean.d.ts +2 -2
  48. package/script/methods/fuzzyClean.d.ts.map +1 -1
  49. package/script/methods/fuzzyClean.js +25 -6
  50. package/script/methods/fuzzyJoin.d.ts +2 -2
  51. package/script/methods/fuzzyJoin.d.ts.map +1 -1
  52. package/script/methods/fuzzyJoin.js +3 -3
  53. package/script/methods/fuzzyJoinQuery.d.ts +1 -1
  54. package/script/methods/fuzzyJoinQuery.d.ts.map +1 -1
  55. package/script/methods/fuzzyJoinQuery.js +13 -3
  56. package/script/methods/loadDataQuery.d.ts +1 -0
  57. package/script/methods/loadDataQuery.d.ts.map +1 -1
  58. package/script/methods/loadDataQuery.js +12 -4
  59. package/script/methods/padQuery.d.ts +5 -0
  60. package/script/methods/padQuery.d.ts.map +1 -0
  61. package/script/methods/padQuery.js +17 -0
  62. package/script/methods/writeGeoDataQuery.d.ts.map +1 -1
  63. package/script/methods/writeGeoDataQuery.js +4 -1
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = writeGeoData;
7
+ const node_fs_1 = require("node:fs");
8
+ const createDirectory_js_1 = __importDefault(require("./createDirectory.js"));
9
+ const getExtension_js_1 = __importDefault(require("./getExtension.js"));
10
+ const findGeoColumn_js_1 = __importDefault(require("./findGeoColumn.js"));
11
+ const hasGeometryColumn_js_1 = __importDefault(require("./hasGeometryColumn.js"));
12
+ const shouldFlipBeforeExport_js_1 = __importDefault(require("./shouldFlipBeforeExport.js"));
13
+ const queryDB_js_1 = __importDefault(require("./queryDB.js"));
14
+ const writeGeoDataQuery_js_1 = __importDefault(require("../methods/writeGeoDataQuery.js"));
15
+ const mergeOptions_js_1 = __importDefault(require("./mergeOptions.js"));
16
+ const rewind_js_1 = __importDefault(require("./rewind.js"));
17
+ const stringifyDates_js_1 = __importDefault(require("./stringifyDates.js"));
18
+ const stringifyDatesInvert_js_1 = __importDefault(require("./stringifyDatesInvert.js"));
19
+ const cleanPath_js_1 = __importDefault(require("./cleanPath.js"));
20
+ async function writeGeoData(table, file, options = {}) {
21
+ (0, createDirectory_js_1.default)(file);
22
+ if (!(await (0, hasGeometryColumn_js_1.default)(table))) {
23
+ throw new Error("Table contains no geometry columns. Use writeData() instead.");
24
+ }
25
+ const fileExtension = (0, getExtension_js_1.default)(file);
26
+ if (fileExtension === "geojson" || fileExtension === "json") {
27
+ let types;
28
+ if (options.formatDates === true) {
29
+ types = await table.getTypes();
30
+ if (Object.values(types).includes("DATE") ||
31
+ Object.values(types).includes("TIMESTAMP")) {
32
+ await (0, stringifyDates_js_1.default)(table, types);
33
+ }
34
+ }
35
+ if (typeof options.compression === "boolean") {
36
+ throw new Error("The compression option is not supported for writing GeoJSON files.");
37
+ }
38
+ const geoColumn = await (0, findGeoColumn_js_1.default)(table);
39
+ const flip = (0, shouldFlipBeforeExport_js_1.default)(table.projections[geoColumn]);
40
+ if (flip) {
41
+ await table.flipCoordinates(geoColumn);
42
+ await (0, queryDB_js_1.default)(table, (0, writeGeoDataQuery_js_1.default)(table.name, file, fileExtension, options), (0, mergeOptions_js_1.default)(table, {
43
+ table: table.name,
44
+ method: "writeGeoData()",
45
+ parameters: { file, options },
46
+ }));
47
+ await table.flipCoordinates(geoColumn);
48
+ }
49
+ else {
50
+ await (0, queryDB_js_1.default)(table, (0, writeGeoDataQuery_js_1.default)(table.name, file, fileExtension, options), (0, mergeOptions_js_1.default)(table, {
51
+ table: table.name,
52
+ method: "writeGeoData()",
53
+ parameters: { file, options },
54
+ }));
55
+ }
56
+ if (options.metadata) {
57
+ const fileData = JSON.parse((0, node_fs_1.readFileSync)(file, "utf-8"));
58
+ fileData.metadata = options.metadata;
59
+ (0, node_fs_1.writeFileSync)(file, JSON.stringify(fileData));
60
+ }
61
+ if (options.rewind) {
62
+ const fileData = JSON.parse((0, node_fs_1.readFileSync)(file, "utf-8"));
63
+ const fileRewinded = (0, rewind_js_1.default)(fileData);
64
+ (0, node_fs_1.writeFileSync)(file, JSON.stringify(fileRewinded));
65
+ }
66
+ if (types && (Object.values(types).includes("DATE") ||
67
+ Object.values(types).includes("TIMESTAMP"))) {
68
+ await (0, stringifyDatesInvert_js_1.default)(table, types);
69
+ }
70
+ }
71
+ else if (fileExtension === "shp") {
72
+ if (typeof options.precision === "number" ||
73
+ typeof options.compression === "boolean" ||
74
+ typeof options.rewind === "boolean" ||
75
+ options.metadata ||
76
+ options.formatDates === true) {
77
+ throw new Error("The following options are not supported for writing SHAPEFILE files: precision, compression, rewind, metadata, and formatDates.");
78
+ }
79
+ const geoColumn = await (0, findGeoColumn_js_1.default)(table);
80
+ const flip = (0, shouldFlipBeforeExport_js_1.default)(table.projections[geoColumn]);
81
+ if (flip) {
82
+ await table.flipCoordinates(geoColumn);
83
+ await (0, queryDB_js_1.default)(table, (0, writeGeoDataQuery_js_1.default)(table.name, file, fileExtension, options), (0, mergeOptions_js_1.default)(table, {
84
+ table: table.name,
85
+ method: "writeGeoData()",
86
+ parameters: { file, options },
87
+ }));
88
+ await table.flipCoordinates(geoColumn);
89
+ }
90
+ else {
91
+ await (0, queryDB_js_1.default)(table, (0, writeGeoDataQuery_js_1.default)(table.name, file, fileExtension, options), (0, mergeOptions_js_1.default)(table, {
92
+ table: table.name,
93
+ method: "writeGeoData()",
94
+ parameters: { file, options },
95
+ }));
96
+ }
97
+ }
98
+ else if (fileExtension === "geoparquet") {
99
+ if (typeof options.precision === "number") {
100
+ throw new Error("The precision option is not supported for writing PARQUET files. Use the .reducePrecision() method.");
101
+ }
102
+ if (typeof options.rewind === "boolean") {
103
+ throw new Error("The rewind option is not supported for writing PARQUET files.");
104
+ }
105
+ await (0, queryDB_js_1.default)(table, `COPY "${table.name}" TO '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT PARQUET${options.compression === true ? ", COMPRESSION 'zstd'" : ""}, KV_METADATA {
106
+ projections: '${JSON.stringify(table.projections)}'
107
+ });`, (0, mergeOptions_js_1.default)(table, {
108
+ table: table.name,
109
+ method: "writeGeoData()",
110
+ parameters: { file, options },
111
+ }));
112
+ }
113
+ else {
114
+ throw new Error(`Unknown extension ${fileExtension}`);
115
+ }
116
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/methods/fill.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,wBAA8B,IAAI,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACnB,iBAiFP"}
1
+ {"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/methods/fill.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAEvD,wBAA8B,IAAI,CAChC,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,EAC1B,OAAO,GAAE;IACP,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACnB,iBAmFP"}
@@ -17,24 +17,19 @@ async function fill(simpleTable, columns, options = {}) {
17
17
  }
18
18
  if (options.interpolate || options.interpolateBy) {
19
19
  const cols = (0, stringToArray_js_1.default)(columns);
20
- let orderCol;
21
- let excludeList;
22
- if (options.interpolateBy) {
23
- orderCol = options.interpolateBy;
24
- excludeList = cols.join(", ");
25
- }
26
- else {
27
- await simpleTable.addRowNumber(tempRowCol);
28
- orderCol = tempRowCol;
29
- excludeList = [`"${tempRowCol}"`, ...cols].join(", ");
30
- }
20
+ // Always add temp row number to preserve original row order in output
21
+ await simpleTable.addRowNumber(tempRowCol);
22
+ // Use interpolateBy for the window function's ORDER BY (correct interpolation math),
23
+ // but always order final output by tempRowCol to preserve input row order
24
+ const windowOrderCol = options.interpolateBy ?? tempRowCol;
25
+ const excludeList = [`"${tempRowCol}"`, ...cols].join(", ");
31
26
  const overClause = categories.length > 0
32
27
  ? `(PARTITION BY ${categories.map((d) => `"${d}"`).join(", ")})`
33
28
  : `()`;
34
29
  const selectList = cols
35
- .map((col) => `fill(${col} ORDER BY "${orderCol}") OVER ${overClause} as ${col}`)
30
+ .map((col) => `fill(${col} ORDER BY "${windowOrderCol}") OVER ${overClause} as ${col}`)
36
31
  .join(", ");
37
- await (0, queryDB_js_1.default)(simpleTable, `CREATE OR REPLACE TABLE "${simpleTable.name}" AS SELECT * EXCLUDE(${excludeList}), ${selectList} FROM "${simpleTable.name}" ORDER BY "${orderCol}";`, (0, mergeOptions_js_1.default)(simpleTable, {
32
+ await (0, queryDB_js_1.default)(simpleTable, `CREATE OR REPLACE TABLE "${simpleTable.name}" AS SELECT * EXCLUDE(${excludeList}), ${selectList} FROM "${simpleTable.name}" ORDER BY "${tempRowCol}";`, (0, mergeOptions_js_1.default)(simpleTable, {
38
33
  table: simpleTable.name,
39
34
  method: "fill()",
40
35
  parameters: { columns, ...options },
@@ -55,9 +50,14 @@ async function fill(simpleTable, columns, options = {}) {
55
50
  }));
56
51
  }
57
52
  else {
58
- await (0, queryDB_js_1.default)(simpleTable, (0, stringToArray_js_1.default)(columns)
59
- .map((col) => `CREATE OR REPLACE TABLE "${simpleTable.name}" AS SELECT * EXCLUDE(${col}), COALESCE(${col}, LAG(${col} IGNORE NULLS) OVER()) as ${col} FROM "${simpleTable.name}";`)
60
- .join("\n"), (0, mergeOptions_js_1.default)(simpleTable, {
53
+ // Simple path: add temp row number and order by it to preserve input row order
54
+ await simpleTable.addRowNumber(tempRowCol);
55
+ const cols = (0, stringToArray_js_1.default)(columns);
56
+ const excludeList = [`"${tempRowCol}"`, ...cols].join(", ");
57
+ const selectList = cols
58
+ .map((col) => `COALESCE(${col}, LAG(${col} IGNORE NULLS) OVER()) as ${col}`)
59
+ .join(", ");
60
+ await (0, queryDB_js_1.default)(simpleTable, `CREATE OR REPLACE TABLE "${simpleTable.name}" AS SELECT * EXCLUDE(${excludeList}), ${selectList} FROM "${simpleTable.name}" ORDER BY "${tempRowCol}";`, (0, mergeOptions_js_1.default)(simpleTable, {
61
61
  table: simpleTable.name,
62
62
  method: "fill()",
63
63
  parameters: { columns, ...options },
@@ -1,7 +1,7 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
- export default function fuzzyClean(table: SimpleTable, column: string, newColumn: string, options?: {
2
+ export default function fuzzyClean(table: SimpleTable, column: string, newColumn: string, threshold: number, options?: {
3
3
  method?: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio";
4
- threshold?: number;
5
4
  keep?: "mostCommon" | "longestString" | "shortestString" | "mostCentral" | "maxScore";
5
+ preFilterPrefixLen?: number;
6
6
  }): Promise<void>;
7
7
  //# sourceMappingURL=fuzzyClean.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fuzzyClean.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyClean.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAIvD,wBAA8B,UAAU,CACtC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EACD,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,aAAa,GACb,UAAU,CAAC;CACX,GACL,OAAO,CAAC,IAAI,CAAC,CAyNf"}
1
+ {"version":3,"file":"fuzzyClean.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyClean.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAIvD,wBAA8B,UAAU,CACtC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,IAAI,CAAC,EACD,YAAY,GACZ,eAAe,GACf,gBAAgB,GAChB,aAAa,GACb,UAAU,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACxB,GACL,OAAO,CAAC,IAAI,CAAC,CAmPf"}
@@ -6,10 +6,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = fuzzyClean;
7
7
  const mergeOptions_js_1 = __importDefault(require("../helpers/mergeOptions.js"));
8
8
  const queryDB_js_1 = __importDefault(require("../helpers/queryDB.js"));
9
- async function fuzzyClean(table, column, newColumn, options = {}) {
9
+ async function fuzzyClean(table, column, newColumn, threshold, options = {}) {
10
10
  const method = options.method ?? "ratio";
11
- const threshold = options.threshold ?? 80;
12
11
  const keep = options.keep ?? "mostCommon";
12
+ let onClause = `rapidfuzz_${method}(a.value, b.value) >= ${threshold}`;
13
+ if (method === "ratio") {
14
+ const maxDiffMultiplier = (200 - 2 * threshold) / (200 - threshold);
15
+ onClause +=
16
+ ` AND ABS(LENGTH(a.value) - LENGTH(b.value)) <= ${maxDiffMultiplier} * GREATEST(LENGTH(a.value), LENGTH(b.value))`;
17
+ }
18
+ if (options.preFilterPrefixLen !== undefined) {
19
+ onClause +=
20
+ ` AND SUBSTR(a.value, 1, ${options.preFilterPrefixLen}) = SUBSTR(b.value, 1, ${options.preFilterPrefixLen})`;
21
+ }
13
22
  // Single round trip: compute fuzzy pairs and embed counts for both sides.
14
23
  // Only values that appear in at least one pair above the threshold can be
15
24
  // normalized — singletons need no processing at all.
@@ -28,16 +37,26 @@ async function fuzzyClean(table, column, newColumn, options = {}) {
28
37
  rapidfuzz_${method}(a.value, b.value) AS score
29
38
  FROM uniques a
30
39
  JOIN uniques b
31
- ON rapidfuzz_${method}(a.value, b.value) >= ${threshold}
40
+ ON ${onClause}
32
41
  AND a.value < b.value`, (0, mergeOptions_js_1.default)(table, {
33
42
  table: table.name,
34
43
  method: "fuzzyClean()",
35
- parameters: { column, newColumn, options },
44
+ parameters: { column, newColumn, threshold, options },
36
45
  returnDataFrom: "query",
37
46
  }));
38
47
  const pairs = pairsData ?? [];
39
- if (pairs.length === 0)
40
- return; // Nothing to normalize.
48
+ if (pairs.length === 0) {
49
+ if (newColumn !== column) {
50
+ await (0, queryDB_js_1.default)(table, `ALTER TABLE "${table.name}" ADD "${newColumn}" VARCHAR;
51
+ UPDATE "${table.name}"
52
+ SET "${newColumn}" = "${column}";`, (0, mergeOptions_js_1.default)(table, {
53
+ table: table.name,
54
+ method: "fuzzyClean()",
55
+ parameters: { column, newColumn, threshold, options },
56
+ }));
57
+ }
58
+ return;
59
+ }
41
60
  // Build count map from the pairs — no separate query needed.
42
61
  const countMap = new Map();
43
62
  for (const { left_value, left_cnt, right_value, right_cnt } of pairs) {
@@ -1,8 +1,8 @@
1
1
  import type SimpleTable from "../class/SimpleTable.js";
2
- export default function fuzzyJoin(leftTable: SimpleTable, rightTable: SimpleTable, leftColumn: string, rightColumn: string, options?: {
2
+ export default function fuzzyJoin(leftTable: SimpleTable, rightTable: SimpleTable, leftColumn: string, rightColumn: string, threshold: number, options?: {
3
3
  method?: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio";
4
- threshold?: number;
5
4
  similarityColumn?: string;
6
5
  outputTable?: string | boolean;
6
+ preFilterPrefixLen?: number;
7
7
  }): Promise<SimpleTable>;
8
8
  //# sourceMappingURL=fuzzyJoin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fuzzyJoin.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,wBAA8B,SAAS,CACrC,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B,wBAuFP"}
1
+ {"version":3,"file":"fuzzyJoin.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAMvD,wBAA8B,SAAS,CACrC,SAAS,EAAE,WAAW,EACtB,UAAU,EAAE,WAAW,EACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE;IACP,MAAM,CAAC,EACH,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACxB,wBAwFP"}
@@ -8,7 +8,7 @@ const getIdenticalColumns_js_1 = __importDefault(require("../helpers/getIdentica
8
8
  const mergeOptions_js_1 = __importDefault(require("../helpers/mergeOptions.js"));
9
9
  const queryDB_js_1 = __importDefault(require("../helpers/queryDB.js"));
10
10
  const fuzzyJoinQuery_js_1 = __importDefault(require("./fuzzyJoinQuery.js"));
11
- async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options = {}) {
11
+ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, threshold, options = {}) {
12
12
  if (leftColumn === rightColumn) {
13
13
  throw new Error(`The leftColumn and rightColumn have the same name "${leftColumn}". Rename one of them before doing the fuzzy join.`);
14
14
  }
@@ -27,13 +27,12 @@ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options
27
27
  }
28
28
  }
29
29
  const method = options.method ?? "ratio";
30
- const threshold = options.threshold ?? 80;
31
30
  const similarityColumn = options.similarityColumn;
32
31
  const outputTableName = typeof options.outputTable === "string"
33
32
  ? options.outputTable
34
33
  : leftTable.name;
35
34
  const sql = `INSTALL rapidfuzz FROM community; LOAD rapidfuzz;\n` +
36
- (0, fuzzyJoinQuery_js_1.default)(leftTable.name, leftColumn, rightTable.name, rightColumn, method, threshold, outputTableName, similarityColumn);
35
+ (0, fuzzyJoinQuery_js_1.default)(leftTable.name, leftColumn, rightTable.name, rightColumn, method, threshold, outputTableName, similarityColumn, options.preFilterPrefixLen);
37
36
  await (0, queryDB_js_1.default)(leftTable, sql, (0, mergeOptions_js_1.default)(leftTable, {
38
37
  table: outputTableName,
39
38
  method: "fuzzyJoin()",
@@ -41,6 +40,7 @@ async function fuzzyJoin(leftTable, rightTable, leftColumn, rightColumn, options
41
40
  leftColumn,
42
41
  rightColumn,
43
42
  rightTable: rightTable.name,
43
+ threshold,
44
44
  options,
45
45
  },
46
46
  }));
@@ -1,2 +1,2 @@
1
- export default function fuzzyJoinQuery(leftTable: string, leftColumn: string, rightTable: string, rightColumn: string, method: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio", threshold: number, outputTable: string, similarityColumn: string | undefined): string;
1
+ export default function fuzzyJoinQuery(leftTable: string, leftColumn: string, rightTable: string, rightColumn: string, method: "ratio" | "partial_ratio" | "token_sort_ratio" | "token_set_ratio", threshold: number, outputTable: string, similarityColumn: string | undefined, preFilterPrefixLen?: number): string;
2
2
  //# sourceMappingURL=fuzzyJoinQuery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fuzzyJoinQuery.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoinQuery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EACF,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GAAG,SAAS,UAsBrC"}
1
+ {"version":3,"file":"fuzzyJoinQuery.d.ts","sourceRoot":"","sources":["../../src/methods/fuzzyJoinQuery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EACF,OAAO,GACP,eAAe,GACf,kBAAkB,GAClB,iBAAiB,EACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,kBAAkB,CAAC,EAAE,MAAM,UAkC5B"}
@@ -1,14 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = fuzzyJoinQuery;
4
- function fuzzyJoinQuery(leftTable, leftColumn, rightTable, rightColumn, method, threshold, outputTable, similarityColumn) {
4
+ function fuzzyJoinQuery(leftTable, leftColumn, rightTable, rightColumn, method, threshold, outputTable, similarityColumn, preFilterPrefixLen) {
5
5
  const fn = `ROUND(rapidfuzz_${method}("${leftTable}"."${leftColumn}", "${rightTable}"."${rightColumn}"), 2)`;
6
+ let onClause = `${fn} >= ${threshold}`;
7
+ if (method === "ratio") {
8
+ const maxDiffMultiplier = (200 - 2 * threshold) / (200 - threshold);
9
+ onClause +=
10
+ ` AND ABS(LENGTH("${leftTable}"."${leftColumn}") - LENGTH("${rightTable}"."${rightColumn}")) <= ${maxDiffMultiplier} * GREATEST(LENGTH("${leftTable}"."${leftColumn}"), LENGTH("${rightTable}"."${rightColumn}"))`;
11
+ }
12
+ if (preFilterPrefixLen !== undefined) {
13
+ onClause +=
14
+ ` AND SUBSTR("${leftTable}"."${leftColumn}", 1, ${preFilterPrefixLen}) = SUBSTR("${rightTable}"."${rightColumn}", 1, ${preFilterPrefixLen})`;
15
+ }
6
16
  if (similarityColumn) {
7
17
  return `CREATE OR REPLACE TABLE "${outputTable}" AS
8
18
  SELECT * EXCLUDE ("_sda_score"), "_sda_score" AS "${similarityColumn}"
9
19
  FROM (
10
20
  SELECT "${leftTable}".*, "${rightTable}".*, ${fn} AS "_sda_score"
11
- FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${fn} >= ${threshold}
21
+ FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${onClause}
12
22
  ) _sda
13
23
  ORDER BY "${leftColumn}", "_sda_score" DESC;\n`;
14
24
  }
@@ -16,7 +26,7 @@ ORDER BY "${leftColumn}", "_sda_score" DESC;\n`;
16
26
  SELECT *
17
27
  FROM (
18
28
  SELECT "${leftTable}".*, "${rightTable}".*
19
- FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${fn} >= ${threshold}
29
+ FROM "${leftTable}" LEFT JOIN "${rightTable}" ON ${onClause}
20
30
  ) _sda
21
31
  ORDER BY "${leftColumn}", "${rightColumn}";\n`;
22
32
  }
@@ -7,6 +7,7 @@ export default function loadDataQuery(table: string, files: string[], options?:
7
7
  columnTypes?: {
8
8
  [key: string]: string;
9
9
  };
10
+ columns?: string[];
10
11
  header?: boolean;
11
12
  allText?: boolean;
12
13
  delim?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"loadDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/loadDataQuery.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,aAAa,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAExC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,cAAc,GAAG,kBAAkB,GAAG,OAAO,CAAC;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,UAwFP"}
1
+ {"version":3,"file":"loadDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/loadDataQuery.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,aAAa,CACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAExC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,cAAc,GAAG,kBAAkB,GAAG,OAAO,CAAC;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,UAmGP"}
@@ -9,6 +9,14 @@ const getExtension_js_1 = __importDefault(require("../helpers/getExtension.js"))
9
9
  function loadDataQuery(table, files, options = {}) {
10
10
  const fileExtension = (0, getExtension_js_1.default)(files[0]);
11
11
  const filesAsString = JSON.stringify(files.map((d) => (0, cleanPath_js_1.default)(d)));
12
+ // Column selection: throw for Excel, build SELECT list for others
13
+ const isExcel = options.fileType === "excel" || fileExtension === "xlsx";
14
+ if (options.columns && options.columns.length > 0 && isExcel) {
15
+ throw new Error("The 'columns' option is not supported for Excel files.");
16
+ }
17
+ const selectColumns = options.columns && options.columns.length > 0
18
+ ? options.columns.map((c) => `"${c}"`).join(", ")
19
+ : "*";
12
20
  // General options, except for parquet
13
21
  const autoDetect = typeof options.autoDetect === "boolean"
14
22
  ? `, auto_detect=${String(options.autoDetect).toUpperCase()}`
@@ -50,7 +58,7 @@ function loadDataQuery(table, files, options = {}) {
50
58
  const encoding = options.encoding ? `, encoding='${options.encoding}'` : "";
51
59
  const strict = options.strict === false ? `, strict_mode=FALSE` : "";
52
60
  return `CREATE OR REPLACE TABLE "${table}"
53
- AS SELECT * FROM read_csv_auto(${filesAsString}${generalOptions}${header}${allText}${delim}${skip}${compression}${encoding}${strict}${nullPadding}${ignoreErrors})${limit};`;
61
+ AS SELECT ${selectColumns} FROM read_csv_auto(${filesAsString}${generalOptions}${header}${allText}${delim}${skip}${compression}${encoding}${strict}${nullPadding}${ignoreErrors})${limit};`;
54
62
  }
55
63
  else if (options.fileType === "json" || fileExtension === "json") {
56
64
  const jsonFormat = options.jsonFormat
@@ -60,10 +68,10 @@ function loadDataQuery(table, files, options = {}) {
60
68
  ? `, records=${String(options.records).toUpperCase()}`
61
69
  : "";
62
70
  return `CREATE OR REPLACE TABLE "${table}"
63
- AS SELECT * FROM read_json_auto(${filesAsString}${generalOptions}${jsonFormat}${records})${limit};`;
71
+ AS SELECT ${selectColumns} FROM read_json_auto(${filesAsString}${generalOptions}${jsonFormat}${records})${limit};`;
64
72
  }
65
73
  else if (options.fileType === "parquet" || fileExtension === "parquet") {
66
- return `CREATE OR REPLACE TABLE "${table}" AS SELECT * FROM read_parquet(${filesAsString}${fileName}${unifyColumns})${limit};`;
74
+ return `CREATE OR REPLACE TABLE "${table}" AS SELECT ${selectColumns} FROM read_parquet(${filesAsString}${fileName}${unifyColumns})${limit};`;
67
75
  }
68
76
  else if (options.fileType === "excel" || fileExtension === "xlsx") {
69
77
  if (files.length > 1) {
@@ -75,7 +83,7 @@ function loadDataQuery(table, files, options = {}) {
75
83
  const allText = typeof options.allText === "boolean"
76
84
  ? `, all_varchar=${String(options.allText).toUpperCase()}`
77
85
  : "";
78
- return `CREATE OR REPLACE TABLE "${table}" AS SELECT * FROM read_xlsx('${files[0]}'${options.sheet ? `, sheet='${options.sheet}'` : ""}${header}${allText});`;
86
+ return `CREATE OR REPLACE TABLE "${table}" AS SELECT ${selectColumns} FROM read_xlsx('${files[0]}'${options.sheet ? `, sheet='${options.sheet}'` : ""}${header}${allText});`;
79
87
  }
80
88
  else {
81
89
  throw new Error(`Unknown options.fileType ${options.fileType} or fileExtension ${fileExtension}`);
@@ -0,0 +1,5 @@
1
+ export default function padQuery(table: string, columns: string[], length: number, options: {
2
+ method?: "left" | "right";
3
+ char?: string;
4
+ }): string;
5
+ //# sourceMappingURL=padQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"padQuery.d.ts","sourceRoot":"","sources":["../../src/methods/padQuery.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,QAAQ,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,UAiBtD"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = padQuery;
4
+ function padQuery(table, columns, length, options) {
5
+ const method = options.method ?? "left";
6
+ const char = options.char ?? "0";
7
+ // Escape single quotes and wrap in single quotes for SQL
8
+ const escapedChar = char.replace(/'/g, "''");
9
+ const paddedChar = `'${escapedChar}'`;
10
+ const func = method === "left" ? "LPAD" : "RPAD";
11
+ let query = "";
12
+ for (const column of columns) {
13
+ query +=
14
+ `\nUPDATE "${table}" SET "${column}" = ${func}("${column}", ${length}, ${paddedChar});`;
15
+ }
16
+ return query;
17
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"writeGeoDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/writeGeoDataQuery.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,UAqBrC"}
1
+ {"version":3,"file":"writeGeoDataQuery.d.ts","sourceRoot":"","sources":["../../src/methods/writeGeoDataQuery.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CACvC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,UAyBrC"}
@@ -12,10 +12,13 @@ function writeGeoDataQuery(table, file, fileExtension, options = {}) {
12
12
  layerOptions.push(`COORDINATE_PRECISION=${options.precision}`);
13
13
  }
14
14
  layerOptions.push(`RFC7946=YES`);
15
- return `COPY "${table}" to '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'GeoJSON'${layerOptions.length > 0
15
+ return `INSTALL spatial; LOAD spatial; COPY "${table}" to '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'GeoJSON'${layerOptions.length > 0
16
16
  ? `, LAYER_CREATION_OPTIONS ('WRITE_NAME=NO', ${layerOptions.map((d) => `'${d}'`).join(", ")})`
17
17
  : ""})`;
18
18
  }
19
+ else if (fileExtension === "shp") {
20
+ return `INSTALL spatial; LOAD spatial; COPY "${table}" TO '${(0, cleanPath_js_1.default)(file)}' WITH (FORMAT GDAL, DRIVER 'ESRI Shapefile', LAYER_CREATION_OPTIONS 'ENCODING=UTF-8')`;
21
+ }
19
22
  else {
20
23
  throw new Error(`Unknown extension ${fileExtension}`);
21
24
  }