|
1 | 1 | import dayjs from 'dayjs'; |
2 | 2 | import { MongoClient } from 'mongodb'; |
3 | | -import { Decimal128 } from 'bson'; |
| 3 | +import { Decimal128, Double } from 'bson'; |
4 | 4 | import { IAdminForthDataSourceConnector, IAdminForthSingleFilter, IAdminForthAndOrFilter, AdminForthResource } from '../types/Back.js'; |
5 | 5 | import AdminForthBaseConnector from './baseConnector.js'; |
6 | 6 |
|
@@ -122,30 +122,30 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
122 | 122 | } |
123 | 123 |
|
124 | 124 | return Array.from(fieldTypes.entries()).map(([name, types]) => { |
125 | | - const primaryKey = name === '_id'; |
126 | | - |
127 | | - const priority = ['datetime', 'date', 'integer', 'float', 'boolean', 'json', 'decimal', 'string']; |
128 | | - |
129 | | - const matched = priority.find(t => types.has(t)) || 'string'; |
130 | | - |
131 | | - const typeMap: Record<string, string> = { |
132 | | - string: 'STRING', |
133 | | - integer: 'INTEGER', |
134 | | - float: 'FLOAT', |
135 | | - boolean: 'BOOLEAN', |
136 | | - datetime: 'DATETIME', |
137 | | - date: 'DATE', |
138 | | - json: 'JSON', |
139 | | - decimal: 'DECIMAL', |
140 | | - }; |
141 | | - return { |
142 | | - name, |
143 | | - type: typeMap[matched] ?? 'STRING', |
144 | | - ...(primaryKey ? { isPrimaryKey: true } : {}), |
145 | | - sampleValue: sampleValues.get(name), |
146 | | - }; |
| 125 | + const primaryKey = name === '_id'; |
| 126 | + |
| 127 | + const priority = ['datetime','date','decimal','integer','float','boolean','json','string']; |
| 128 | + |
| 129 | + const matched = priority.find(t => types.has(t)) || 'string'; |
| 130 | + |
| 131 | + const typeMap: Record<string, AdminForthDataTypes> = { |
| 132 | + string: AdminForthDataTypes.STRING, |
| 133 | + integer: AdminForthDataTypes.INTEGER, |
| 134 | + float: AdminForthDataTypes.FLOAT, |
| 135 | + boolean: AdminForthDataTypes.BOOLEAN, |
| 136 | + datetime: AdminForthDataTypes.DATETIME, |
| 137 | + date: AdminForthDataTypes.DATE, |
| 138 | + json: AdminForthDataTypes.JSON, |
| 139 | + decimal: AdminForthDataTypes.DECIMAL, |
| 140 | + }; |
| 141 | + return { |
| 142 | + name, |
| 143 | + type: typeMap[matched] ?? AdminForthDataTypes.STRING, |
| 144 | + ...(primaryKey ? { isPrimaryKey: true } : {}), |
| 145 | + sampleValue: sampleValues.get(name), |
| 146 | + }; |
147 | 147 | }); |
148 | | - } |
| 148 | + } |
149 | 149 |
|
150 | 150 |
|
151 | 151 | async discoverFields(resource) { |
@@ -200,19 +200,34 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
200 | 200 |
|
201 | 201 |
|
202 | 202 | setFieldValue(field, value) { |
203 | | - if (field.type == AdminForthDataTypes.DATETIME) { |
204 | | - if (!value) { |
205 | | - return null; |
206 | | - } |
207 | | - return dayjs(value).toDate(); |
208 | | - |
209 | | - } else if (field.type == AdminForthDataTypes.BOOLEAN) { |
210 | | - return value === null ? null : (value ? true : false); |
211 | | - } else if (field.type == AdminForthDataTypes.DECIMAL) { |
212 | | - if (value === null || value === undefined) { |
213 | | - return null; |
214 | | - } |
215 | | - return Decimal128.fromString(value?.toString()); |
| 203 | + if (value === undefined) return undefined; |
| 204 | + if (value === null || value === "") return null; |
| 205 | + |
| 206 | + if (field.type === AdminForthDataTypes.DATETIME) { |
| 207 | + return dayjs(value).isValid() ? dayjs(value).toDate() : null; |
| 208 | + } |
| 209 | + |
| 210 | + if (field.type === AdminForthDataTypes.DATE) { |
| 211 | + const d = dayjs(value); |
| 212 | + return d.isValid() ? d.startOf("day").toDate() : null; |
| 213 | + } |
| 214 | + |
| 215 | + if (field.type === AdminForthDataTypes.BOOLEAN) { |
| 216 | + return value === null ? null : !!value; |
| 217 | + } |
| 218 | + |
| 219 | + if (field.type === AdminForthDataTypes.INTEGER) { |
| 220 | + const n = typeof value === "number" ? value : Number(String(value).replace(",", ".")); |
| 221 | + return Number.isFinite(n) ? Math.trunc(n) : null; |
| 222 | + } |
| 223 | + |
| 224 | + if (field.type === AdminForthDataTypes.FLOAT) { |
| 225 | + const n = typeof value === "number" ? value : Number(String(value).replace(",", ".")); |
| 226 | + return Number.isFinite(n) ? new Double(n) : null; |
| 227 | + } |
| 228 | + |
| 229 | + if (field.type === AdminForthDataTypes.DECIMAL) { |
| 230 | + return Decimal128.fromString(value.toString()); |
216 | 231 | } |
217 | 232 | return value; |
218 | 233 | } |
@@ -251,7 +266,7 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
251 | 266 | return { $expr: { [mongoExprOp]: [left, right] } }; |
252 | 267 | } |
253 | 268 | const column = resource.dataSourceColumns.find((col) => col.name === (filter as IAdminForthSingleFilter).field); |
254 | | - if (['integer', 'decimal', 'float'].includes(column.type)) { |
| 269 | + if ([AdminForthDataTypes.INTEGER, AdminForthDataTypes.DECIMAL, AdminForthDataTypes.FLOAT].includes(column.type)) { |
255 | 270 | return { [(filter as IAdminForthSingleFilter).field]: this.OperatorsMap[filter.operator](+(filter as IAdminForthSingleFilter).value) }; |
256 | 271 | } |
257 | 272 | return { [(filter as IAdminForthSingleFilter).field]: this.OperatorsMap[filter.operator]((filter as IAdminForthSingleFilter).value) }; |
@@ -315,30 +330,36 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
315 | 330 | const collection = this.client.db().collection(tableName); |
316 | 331 | const result = {}; |
317 | 332 | for (const column of columns) { |
318 | | - result[column] = await collection |
| 333 | + result[column.name] = await collection |
319 | 334 | .aggregate([ |
320 | | - { $group: { _id: null, min: { $min: `$${column}` }, max: { $max: `$${column}` } } }, |
| 335 | + { $group: { _id: null, min: { $min: `$${column.name}` }, max: { $max: `$${column.name}` } } }, |
321 | 336 | ]) |
322 | 337 | .toArray(); |
323 | 338 | } |
324 | 339 | return result; |
325 | 340 | } |
326 | 341 |
|
327 | 342 | async createRecordOriginalValues({ resource, record }): Promise<string> { |
328 | | - const tableName = resource.table; |
329 | | - const collection = this.client.db().collection(tableName); |
330 | | - const columns = Object.keys(record); |
| 343 | + const collection = this.client.db().collection(resource.table); |
| 344 | + const colsByName = new Map(resource.dataSourceColumns.map((c) => [c.name, c])); |
331 | 345 | const newRecord = {}; |
332 | | - for (const colName of columns) { |
333 | | - newRecord[colName] = record[colName]; |
| 346 | + for (const [key, raw] of Object.entries(record)) { |
| 347 | + const col = colsByName.get(key); |
| 348 | + newRecord[key] = col ? this.setFieldValue(col, raw) : raw; |
334 | 349 | } |
335 | 350 | const ret = await collection.insertOne(newRecord); |
336 | 351 | return ret.insertedId; |
337 | 352 | } |
338 | 353 |
|
339 | 354 | async updateRecordOriginalValues({ resource, recordId, newValues }) { |
340 | 355 | const collection = this.client.db().collection(resource.table); |
341 | | - await collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: newValues }); |
| 356 | + const colsByName = new Map(resource.dataSourceColumns.map((c) => [c.name, c])); |
| 357 | + const updatedValues = {}; |
| 358 | + for (const [key, raw] of Object.entries(newValues)) { |
| 359 | + const col = colsByName.get(key); |
| 360 | + updatedValues[key] = col ? this.setFieldValue(col, raw) : raw; |
| 361 | + } |
| 362 | + await collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: updatedValues }); |
342 | 363 | } |
343 | 364 |
|
344 | 365 | async deleteRecord({ resource, recordId }): Promise<boolean> { |
|
0 commit comments