|
1 | 1 | import dayjs from 'dayjs'; |
2 | | -import { MongoClient } from 'mongodb'; |
3 | | -import { Decimal128, Double, ObjectId } from 'bson'; |
4 | | -import { IAdminForthDataSourceConnector, IAdminForthSingleFilter, IAdminForthAndOrFilter, AdminForthResource } from '../types/Back.js'; |
| 2 | +import { MongoClient, BSON, ObjectId, Decimal128, Double, UUID } from 'mongodb'; |
| 3 | +import { IAdminForthDataSourceConnector, IAdminForthSingleFilter, IAdminForthAndOrFilter, AdminForthResource, Filters } from '../types/Back.js'; |
5 | 4 | import AdminForthBaseConnector from './baseConnector.js'; |
6 | | - |
7 | 5 | import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, } from '../types/Common.js'; |
8 | 6 |
|
| 7 | +function idToString(v: any) { |
| 8 | + if (v == null) return null; |
| 9 | + if (typeof v === "string" || typeof v === "number" || typeof v === "bigint") return String(v); |
| 10 | + |
| 11 | + const s = BSON.EJSON.serialize(v); |
| 12 | + if (s && typeof s === "object") { |
| 13 | + if ("$oid" in s) { |
| 14 | + return String(s.$oid); |
| 15 | + } |
| 16 | + if ("$uuid" in s) { |
| 17 | + return String(s.$uuid); |
| 18 | + } |
| 19 | + return String(v); |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +const extractSimplePkEq = (filters: IAdminForthAndOrFilter, pk: string): string | null => { |
| 24 | + if (!filters?.subFilters?.length) return null; |
| 25 | + if (filters.operator !== AdminForthFilterOperators.AND) return null; |
| 26 | + if (filters.subFilters.length !== 1) return null; |
| 27 | + |
| 28 | + const f: any = filters.subFilters[0]; |
| 29 | + if (!f?.field) return null; |
| 30 | + if (f.field !== pk) return null; |
| 31 | + if (f.operator !== AdminForthFilterOperators.EQ) return null; |
| 32 | + if (typeof f.value !== "string") return null; |
| 33 | + |
| 34 | + return f.value; |
| 35 | +} |
| 36 | + |
9 | 37 | const escapeRegex = (value) => { |
10 | 38 | return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escapes special characters |
11 | 39 | }; |
@@ -184,85 +212,44 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
184 | 212 | } |
185 | 213 |
|
186 | 214 | getFieldValue(field, value) { |
187 | | - if (field.type == AdminForthDataTypes.DATETIME) { |
188 | | - if (!value) { |
189 | | - return null; |
190 | | - } |
191 | | - return dayjs(Date.parse(value)).toISOString(); |
192 | | - |
193 | | - } else if (field.type == AdminForthDataTypes.DATE) { |
194 | | - if (!value) { |
195 | | - return null; |
196 | | - } |
197 | | - return dayjs(Date.parse(value)).toISOString().split('T')[0]; |
198 | | - |
199 | | - } else if (field.type == AdminForthDataTypes.BOOLEAN) { |
200 | | - return value === null ? null : !!value; |
201 | | - } else if (field.type == AdminForthDataTypes.DECIMAL) { |
202 | | - if (value === null || value === undefined) { |
203 | | - return null; |
204 | | - } |
205 | | - return value?.toString(); |
206 | | - } else if (field.name === '_id' && !field.fillOnCreate) { |
207 | | - // value is supposed to be an ObjectId or string representing it |
208 | | - if (typeof value === 'object') { |
209 | | - return value?.toString(); |
210 | | - } |
| 215 | + if (field.type === AdminForthDataTypes.DATETIME) { |
| 216 | + return value ? dayjs(Date.parse(value)).toISOString() : null; |
| 217 | + } |
| 218 | + if (field.type === AdminForthDataTypes.DATE) { |
| 219 | + return value ? dayjs(Date.parse(value)).toISOString().split("T")[0] : null; |
| 220 | + } |
| 221 | + if (field.type === AdminForthDataTypes.BOOLEAN) { |
| 222 | + return value === null ? null : !!value; |
| 223 | + } |
| 224 | + if (field.type === AdminForthDataTypes.DECIMAL) { |
| 225 | + return value === null || value === undefined ? null : value.toString(); |
| 226 | + } |
| 227 | + if (field.name === '_id') { |
| 228 | + return idToString(value); |
211 | 229 | } |
212 | | - |
213 | 230 | return value; |
214 | 231 | } |
215 | 232 |
|
216 | 233 |
|
217 | 234 | setFieldValue(field, value) { |
218 | | - if (value === undefined) return undefined; |
219 | | - if (value === null) return null; |
220 | | - |
| 235 | + if (value === undefined) { |
| 236 | + return undefined; |
| 237 | + } |
| 238 | + if (value === null || value === '') { |
| 239 | + return null; |
| 240 | + } |
221 | 241 | if (field.type === AdminForthDataTypes.DATETIME) { |
222 | | - if (value === "" || value === null) { |
223 | | - return null; |
224 | | - } |
225 | 242 | return dayjs(value).isValid() ? dayjs(value).toDate() : null; |
226 | 243 | } |
227 | | - |
228 | 244 | if (field.type === AdminForthDataTypes.INTEGER) { |
229 | | - if (value === "" || value === null) { |
230 | | - return null; |
231 | | - } |
232 | 245 | return Number.isFinite(value) ? Math.trunc(value) : null; |
233 | 246 | } |
234 | | - |
235 | 247 | if (field.type === AdminForthDataTypes.FLOAT) { |
236 | | - if (value === "" || value === null) { |
237 | | - return null; |
238 | | - } |
239 | 248 | return Number.isFinite(value) ? value : null; |
240 | 249 | } |
241 | | - |
242 | 250 | if (field.type === AdminForthDataTypes.DECIMAL) { |
243 | | - if (value === "" || value === null) { |
244 | | - return null; |
245 | | - } |
246 | 251 | return value.toString(); |
247 | 252 | } |
248 | | - |
249 | | - if (field.name === '_id' && !field.fillOnCreate) { |
250 | | - // value is supposed to be an ObjectId |
251 | | - if (!ObjectId.isValid(value)) { |
252 | | - return null; |
253 | | - } |
254 | | - if (typeof value === 'string' || typeof value === 'number') { |
255 | | - // if string or number - turn it into ObjectId |
256 | | - return new ObjectId(value); |
257 | | - } else if (typeof value === 'object') { |
258 | | - // assume it is a correct ObjectId |
259 | | - return value; |
260 | | - } |
261 | | - |
262 | | - // unsupported type for ObjectId |
263 | | - return null; |
264 | | - } |
265 | | - |
266 | 253 | return value; |
267 | 254 | } |
268 | 255 |
|
@@ -313,34 +300,53 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
313 | 300 | .map((f) => this.getFilterQuery(resource, f))); |
314 | 301 | } |
315 | 302 |
|
316 | | - async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: |
317 | | - { |
318 | | - resource: AdminForthResource, |
319 | | - limit: number, |
320 | | - offset: number, |
321 | | - sort: { field: string, direction: AdminForthSortDirections }[], |
| 303 | + async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: |
| 304 | + { |
| 305 | + resource: AdminForthResource, |
| 306 | + limit: number, |
| 307 | + offset: number, |
| 308 | + sort: { field: string, direction: AdminForthSortDirections }[], |
322 | 309 | filters: IAdminForthAndOrFilter, |
323 | 310 | } |
324 | 311 | ): Promise<any[]> { |
325 | 312 |
|
326 | 313 | // const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', '); |
327 | 314 | const tableName = resource.table; |
| 315 | + const collection = this.client.db().collection(tableName); |
328 | 316 |
|
329 | 317 |
|
330 | | - const collection = this.client.db().collection(tableName); |
| 318 | + const pk = this.getPrimaryKey(resource); |
| 319 | + const pkValue = extractSimplePkEq(filters, pk); |
| 320 | + |
| 321 | + if (pkValue !== null) { |
| 322 | + let res = await collection.find({ [pk]: pkValue }).limit(1).toArray(); |
| 323 | + if (res.length) { |
| 324 | + return res; |
| 325 | + } |
| 326 | + try { |
| 327 | + res = await collection.find({ [pk]: new UUID(pkValue) }).limit(1).toArray(); |
| 328 | + if (res.length) { |
| 329 | + return res; |
| 330 | + } |
| 331 | + } catch {} |
| 332 | + try { |
| 333 | + res = await collection.find({ [pk]: new ObjectId(pkValue) }).limit(1).toArray(); |
| 334 | + if (res.length) { |
| 335 | + return res; |
| 336 | + } |
| 337 | + } catch {} |
| 338 | + return []; |
| 339 | + } |
| 340 | + |
331 | 341 | const query = filters.subFilters.length ? this.getFilterQuery(resource, filters) : {}; |
332 | 342 |
|
333 | | - const sortArray: any[] = sort.map((s) => { |
334 | | - return [s.field, this.SortDirectionsMap[s.direction]]; |
335 | | - }); |
| 343 | + const sortArray: any[] = sort.map((s) => [s.field, this.SortDirectionsMap[s.direction]]); |
336 | 344 |
|
337 | | - const result = await collection.find(query) |
| 345 | + return await collection.find(query) |
338 | 346 | .sort(sortArray) |
339 | 347 | .skip(offset) |
340 | 348 | .limit(limit) |
341 | 349 | .toArray(); |
342 | | - |
343 | | - return result |
344 | 350 | } |
345 | 351 |
|
346 | 352 | async getCount({ resource, filters }: { |
@@ -393,15 +399,17 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS |
393 | 399 | } |
394 | 400 |
|
395 | 401 | async updateRecordOriginalValues({ resource, recordId, newValues }) { |
396 | | - const collection = this.client.db().collection(resource.table); |
397 | | - const primaryKeyColumn = resource.dataSourceColumns.find((col) => col.name === this.getPrimaryKey(resource)); |
398 | | - await collection.updateOne({ [primaryKeyColumn.name]: this.setFieldValue(primaryKeyColumn, recordId) }, { $set: newValues }); |
| 402 | + const collection = this.client.db().collection(resource.table); |
| 403 | + const pk = this.getPrimaryKey(resource); |
| 404 | + const rows = await this.getDataWithOriginalTypes({resource, limit: 1, offset: 0, sort: [], filters: Filters.AND(Filters.EQ(pk, recordId))}); |
| 405 | + await collection.updateOne({ [pk]: rows[0][pk] || recordId }, { $set: newValues }); |
399 | 406 | } |
400 | 407 |
|
401 | 408 | async deleteRecord({ resource, recordId }): Promise<boolean> { |
402 | 409 | const collection = this.client.db().collection(resource.table); |
403 | | - const primaryKeyColumn = resource.dataSourceColumns.find((col) => col.name === this.getPrimaryKey(resource)); |
404 | | - const res = await collection.deleteOne({ [primaryKeyColumn.name]: this.setFieldValue(primaryKeyColumn, recordId) }); |
| 410 | + const pk = this.getPrimaryKey(resource); |
| 411 | + const rows = await this.getDataWithOriginalTypes({resource, limit: 1, offset: 0, sort: [], filters: Filters.AND(Filters.EQ(pk, recordId))}); |
| 412 | + const res = await collection.deleteOne({ [pk]: rows[0][pk] }); |
405 | 413 | return res.deletedCount > 0; |
406 | 414 | } |
407 | 415 |
|
|
0 commit comments