Skip to content

Commit d28ef0b

Browse files
committed
fix unflattening
1 parent 57c3b82 commit d28ef0b

2 files changed

Lines changed: 21 additions & 153 deletions

File tree

src/render.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ export const generate_query = ({
101101
exec += `);\n`;
102102

103103
if (nested_schema != null) {
104-
exec += ` return unflatten_sql_results(rows as unknown as SqlRow[], ${JSON.stringify(
105-
nested_schema,
106-
)}) as unknown as ${result};\n`;
104+
exec += ` return unflatten_sql_results(rows, ${JSON.stringify(nested_schema)}) as unknown as ${result};\n`;
107105
} else {
108106
exec += ` return rows as unknown as ${result};\n`;
109107
}
@@ -193,7 +191,7 @@ const column_to_tstype = ({
193191
const { type } = get_column_type({ config, column, schema_types });
194192
let final_type = type;
195193

196-
if (column.is_array) {
194+
if (column.is_array || column.is_sqlc_slice) {
197195
final_type = `Array<${type}>`;
198196
}
199197

@@ -284,14 +282,9 @@ type SchemaNode = {
284282
original_name?: string;
285283
};
286284
287-
type SqlRow = {
288-
[key: string]: unknown;
289-
'[]': number;
290-
};
291-
292285
type NestedSchema = Record<string, SchemaNode>;
293286
294-
const unflatten_row = (row: SqlRow, schema: NestedSchema): Record<string, unknown> => {
287+
const unflatten_row = (row: any, schema: NestedSchema) => {
295288
// Recursive helper function to build nested objects
296289
const build_object = (schema: NestedSchema, prefix = ''): Record<string, unknown> => {
297290
const result: Record<string, unknown> = {};
@@ -372,7 +365,7 @@ const merge_objects = (target: IdentifiableObject, source: IdentifiableObject):
372365
return target;
373366
};
374367
375-
const unflatten_sql_results = (rows: SqlRow[], schema: NestedSchema): IdentifiableObject[] => {
368+
const unflatten_sql_results = (rows: any[], schema: NestedSchema): IdentifiableObject[] => {
376369
if (rows.length === 0) {
377370
return [];
378371
}
@@ -382,17 +375,17 @@ const unflatten_sql_results = (rows: SqlRow[], schema: NestedSchema): Identifiab
382375
383376
// Step 3: Merge rows based on root array indicator
384377
const result: IdentifiableObject[] = [];
385-
const root_index_map = new Map<number, number>();
378+
const root_id_map = new Map<unknown, number>();
386379
387-
for (const row of unflattened_rows) {
388-
const root_index = rows[0]?.['[]'];
389-
if (typeof root_index !== 'number') {
390-
throw new Error('Invalid root index');
380+
for (const [index, row] of unflattened_rows.entries()) {
381+
const root_key = rows[index]?.['[]'];
382+
if (root_key == null) {
383+
throw new Error('Invalid root key');
391384
}
392385
393-
const existing_index = root_index_map.get(root_index);
394-
if (existing_index === undefined) {
395-
root_index_map.set(root_index, result.length);
386+
const existing_index = root_id_map.get(root_key);
387+
if (existing_index == null) {
388+
root_id_map.set(root_key, result.length);
396389
result.push(row);
397390
} else if (result[existing_index] != null) {
398391
merge_objects(result[existing_index], row);

src/unflatten.ts

Lines changed: 9 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,143 +1,20 @@
1-
type SchemaType = 'value' | 'object' | 'array';
2-
3-
interface SchemaNode {
4-
type: SchemaType;
5-
properties?: SchemaProperties;
1+
type SchemaNode = {
2+
type: 'value' | 'object' | 'array';
3+
properties?: Record<string, SchemaNode>;
64
original_name?: string;
7-
}
8-
9-
interface SchemaProperties {
10-
[key: string]: SchemaNode;
11-
}
12-
13-
export interface NestedSchema {
14-
[key: string]: SchemaNode;
15-
}
16-
17-
type SqlRow = {
18-
[key: string]: unknown;
19-
'[]': number;
20-
};
21-
22-
const unflatten_row = (row: SqlRow, schema: NestedSchema): Record<string, unknown> => {
23-
// Recursive helper function to build nested objects
24-
const build_object = (schema: NestedSchema, prefix = ''): Record<string, unknown> => {
25-
const result: Record<string, unknown> = {};
26-
27-
for (const [key, value] of Object.entries(schema)) {
28-
const full_key = prefix ? prefix + '.' + key : key;
29-
30-
// Handle different types from schema
31-
if (value.type === 'value') {
32-
// Direct value assignment for regular fields
33-
result[key] = row[full_key];
34-
} else if (value.type === 'object' && value.properties) {
35-
// Recursive object building for nested structures
36-
result[key] = build_object(value.properties, full_key);
37-
} else if (value.type === 'array' && value.properties) {
38-
// Handle array items - create empty array if null, otherwise build object
39-
const array_index = row[full_key + '[]'];
40-
if (array_index === null) {
41-
result[key] = [];
42-
} else {
43-
result[key] = [build_object(value.properties, full_key + '[]')];
44-
}
45-
}
46-
}
47-
48-
return result;
49-
};
50-
51-
return build_object(schema);
52-
};
53-
54-
interface IdentifiableObject {
55-
id?: unknown;
56-
[key: string]: unknown;
57-
}
58-
59-
const merge_objects = (target: IdentifiableObject, source: IdentifiableObject): IdentifiableObject => {
60-
for (const [key, value] of Object.entries(source)) {
61-
if (Array.isArray(value)) {
62-
// Initialize array if doesn't exist
63-
const target_array = target[key];
64-
if (!target_array) {
65-
target[key] = [];
66-
} else if (!Array.isArray(target_array)) {
67-
throw new Error('Expected array at key ' + key);
68-
}
69-
70-
// Merge array items if not empty
71-
if (value.length > 0) {
72-
const first_value = value[0] as IdentifiableObject;
73-
const target_typed = target[key] as IdentifiableObject[];
74-
75-
const existing_item = target_typed.find((item) => item.id === first_value.id);
76-
77-
if (existing_item) {
78-
// Merge into existing item if found
79-
merge_objects(existing_item, first_value);
80-
} else {
81-
// Add new items if not found
82-
target_typed.push(...(value as IdentifiableObject[]));
83-
}
84-
}
85-
} else if (typeof value === 'object' && value !== null) {
86-
// Handle nested objects recursively
87-
if (!target[key]) {
88-
target[key] = {};
89-
}
90-
const target_obj = target[key];
91-
if (typeof target_obj !== 'object' || target_obj === null) {
92-
throw new Error('Expected object at key ' + key);
93-
}
94-
merge_objects(target_obj as IdentifiableObject, value as IdentifiableObject);
95-
} else {
96-
// Direct assignment for primitive values
97-
target[key] = value;
98-
}
99-
}
100-
return target;
1015
};
1026

103-
const unflatten_sql_results = (rows: SqlRow[]): IdentifiableObject[] => {
104-
if (rows[0] == null) {
105-
return [];
106-
}
107-
108-
// Step 1: Extract schema from first row
109-
const schema = extract_nested_schema(Object.keys(rows[0]));
110-
111-
// Step 2: Unflatten each row according to schema
112-
const unflattened_rows = rows.map((row) => unflatten_row(row, schema) as IdentifiableObject);
113-
114-
// Step 3: Merge rows based on root array indicator
115-
const result: IdentifiableObject[] = [];
116-
const root_index_map = new Map<number, number>();
117-
118-
for (const row of unflattened_rows) {
119-
const root_index = rows[0]['[]'];
120-
if (typeof root_index !== 'number') {
121-
throw new Error('Invalid root index');
122-
}
123-
124-
const existing_index = root_index_map.get(root_index);
125-
if (existing_index === undefined) {
126-
root_index_map.set(root_index, result.length);
127-
result.push(row);
128-
} else if (result[existing_index] != null) {
129-
merge_objects(result[existing_index], row);
130-
}
131-
}
132-
133-
return result;
134-
};
7+
type NestedSchema = Record<string, SchemaNode>;
1358

1369
export const extract_nested_schema = (keys: string[]): NestedSchema => {
13710
const schema: NestedSchema = {};
13811

12+
const root_key = keys.find((key) => key === '[]');
13+
if (!root_key) {
14+
throw new Error('Root key not found');
15+
}
16+
13917
for (const key of keys) {
140-
// Skip the root array indicator as it's used for grouping only
14118
if (key === '[]') {
14219
continue;
14320
}
@@ -195,5 +72,3 @@ export const extract_nested_schema = (keys: string[]): NestedSchema => {
19572

19673
return schema;
19774
};
198-
199-
export { unflatten_sql_results, type IdentifiableObject, type SqlRow };

0 commit comments

Comments
 (0)