Skip to content

Commit 8b7ea42

Browse files
chore: wip
1 parent d8aa407 commit 8b7ea42

35 files changed

+527
-13993
lines changed

storage/framework/core/orm/src/base.ts

Lines changed: 213 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -500,32 +500,22 @@ export class BaseOrm<T, C, J> {
500500
return this.applyHaving<V>(column, operator, value)
501501
}
502502

503-
applyInRandomOrder(): this {
504-
this.selectFromQuery = this.selectFromQuery.orderBy(sql` ${sql.raw('RANDOM()')} `)
505-
506-
return this
507-
}
508-
509-
inRandomOrder(): this {
510-
return this.applyInRandomOrder()
511-
}
512-
513503
applyOrderByDesc(column: keyof C): this {
514504
this.selectFromQuery = this.selectFromQuery.orderBy(column, 'desc')
515505

516506
return this
517507
}
518508

509+
orderByDesc(column: keyof C): this {
510+
return this.applyOrderByDesc(column)
511+
}
512+
519513
applyOrderByAsc(column: keyof C): this {
520514
this.selectFromQuery = this.selectFromQuery.orderBy(column, 'asc')
521515

522516
return this
523517
}
524518

525-
orderByDesc(column: keyof C): this {
526-
return this.applyOrderByDesc(column)
527-
}
528-
529519
orderByAsc(column: keyof C): this {
530520
return this.applyOrderByAsc(column)
531521
}
@@ -552,20 +542,220 @@ export class BaseOrm<T, C, J> {
552542
return this.applyJoin(table, firstCol, secondCol)
553543
}
554544

555-
async applyUpdate<U>(values: U): Promise<T | undefined> {
556-
if (!values || Object.keys(values).length === 0)
557-
return undefined
545+
async applyPluck<K extends keyof T>(field: K): Promise<T[K][]> {
546+
let models
547+
548+
if (this.hasSelect) {
549+
models = await this.selectFromQuery.execute()
550+
}
551+
else {
552+
models = await this.selectFromQuery.selectAll().execute()
553+
}
554+
555+
return models.map((model: T) => model[field])
556+
}
557+
558+
async pluck<K extends keyof T>(field: K): Promise<T[K][]> {
559+
return await this.applyPluck(field)
560+
}
561+
562+
applyInRandomOrder(): this {
563+
this.selectFromQuery = this.selectFromQuery.orderBy(sql` ${sql.raw('RANDOM()')} `)
564+
565+
return this
566+
}
567+
568+
inRandomOrder(): this {
569+
return this.applyInRandomOrder()
570+
}
571+
572+
applyWhereExists(callback: (qb: any) => any): this {
573+
this.selectFromQuery = this.selectFromQuery.where(({ exists, selectFrom }: any) =>
574+
exists(callback({ exists, selectFrom })),
575+
)
576+
577+
return this
578+
}
579+
580+
whereExists(callback: (qb: any) => any): this {
581+
return this.applyWhereExists(callback)
582+
}
583+
584+
applyHas(relation: string): this {
585+
this.selectFromQuery = this.selectFromQuery.where(({ exists, selectFrom }: any) =>
586+
exists(
587+
selectFrom(relation)
588+
.select('1')
589+
.whereRef(`${relation}.${this.tableName.slice(0, -1)}_id`, '=', `${this.tableName}.id`),
590+
),
591+
)
592+
593+
return this
594+
}
595+
596+
has(relation: string): this {
597+
return this.applyHas(relation)
598+
}
599+
600+
applyDoesntHave(relation: string): this {
601+
this.selectFromQuery = this.selectFromQuery.where(({ not, exists, selectFrom }: any) =>
602+
not(
603+
exists(
604+
selectFrom(relation)
605+
.select('1')
606+
.whereRef(`${relation}.${this.tableName.slice(0, -1)}_id`, '=', `${this.tableName}.id`),
607+
),
608+
),
609+
)
558610

559-
await this.updateFromQuery
560-
.set(values as any)
611+
return this
612+
}
613+
614+
doesntHave(relation: string): this {
615+
return this.applyDoesntHave(relation)
616+
}
617+
618+
applyWhereHas(relation: string, callback: (query: any) => void): this {
619+
this.selectFromQuery = this.selectFromQuery
620+
.where(({ exists, selectFrom }: any) => {
621+
const subquery = selectFrom(relation)
622+
.select('1')
623+
.whereRef(`${relation}.${this.tableName.slice(0, -1)}_id`, '=', `${this.tableName}.id`)
624+
625+
// Apply the callback to the subquery
626+
callback(subquery)
627+
628+
return exists(subquery)
629+
})
630+
631+
return this
632+
}
633+
634+
whereHas(relation: string, callback: (query: any) => void): this {
635+
return this.applyWhereHas(relation, callback)
636+
}
637+
638+
applyWhereDoesntHave(relation: string, callback: (query: any) => void): this {
639+
this.selectFromQuery = this.selectFromQuery
640+
.where(({ exists, selectFrom, not }: any) => {
641+
const subquery = selectFrom(relation)
642+
.select('1')
643+
.whereRef(`${relation}.${this.tableName.slice(0, -1)}_id`, '=', `${this.tableName}.id`)
644+
645+
// Apply the callback to the subquery
646+
callback(subquery)
647+
648+
return not(exists(subquery))
649+
})
650+
651+
return this
652+
}
653+
654+
whereDoesntHave(relation: string, callback: (query: any) => void): this {
655+
return this.applyWhereDoesntHave(relation, callback)
656+
}
657+
658+
async applyPaginate(options: { limit?: number, offset?: number, page?: number } = { limit: 10, offset: 0, page: 1 }): Promise<{ data: T[], paging: { total_records: number, page: number, total_pages: number }, next_cursor: number | null }> {
659+
const totalRecordsResult = await DB.instance.selectFrom(this.tableName)
660+
.select(DB.instance.fn.count('id').as('total'))
661+
.executeTakeFirst()
662+
663+
const totalRecords = Number(totalRecordsResult?.total) || 0
664+
const totalPages = Math.ceil(totalRecords / (options.limit ?? 10))
665+
666+
const modelsWithExtra = await DB.instance.selectFrom(this.tableName)
667+
.selectAll()
668+
.orderBy('id', 'asc')
669+
.limit((options.limit ?? 10) + 1)
670+
.offset(((options.page ?? 1) - 1) * (options.limit ?? 10))
671+
.execute()
672+
673+
let nextCursor = null
674+
if (modelsWithExtra.length > (options.limit ?? 10))
675+
nextCursor = modelsWithExtra.pop()?.id ?? null
676+
677+
this.mapCustomGetters(modelsWithExtra)
678+
await this.loadRelations(modelsWithExtra)
679+
680+
return {
681+
data: modelsWithExtra as T[],
682+
paging: {
683+
total_records: totalRecords,
684+
page: options.page || 1,
685+
total_pages: totalPages,
686+
},
687+
next_cursor: nextCursor,
688+
}
689+
}
690+
691+
async paginate(options: { limit?: number, offset?: number, page?: number } = { limit: 10, offset: 0, page: 1 }): Promise<{ data: T[], paging: { total_records: number, page: number, total_pages: number }, next_cursor: number | null }> {
692+
return await this.applyPaginate(options)
693+
}
694+
695+
async applyMax(field: keyof C): Promise<number> {
696+
const result = await this.selectFromQuery
697+
.select(sql`MAX(${sql.raw(field as string)}) as max`)
698+
.executeTakeFirst()
699+
700+
return result.max || 0
701+
}
702+
703+
async max(field: keyof C): Promise<number> {
704+
return await this.applyMax(field)
705+
}
706+
707+
async applyMin(field: keyof C): Promise<number> {
708+
const result = await this.selectFromQuery
709+
.select(sql`MIN(${sql.raw(field as string)}) as min`)
710+
.executeTakeFirst()
711+
712+
return result.min || 0
713+
}
714+
715+
async min(field: keyof C): Promise<number> {
716+
return await this.applyMin(field)
717+
}
718+
719+
async applyAvg(field: keyof C): Promise<number> {
720+
const result = await this.selectFromQuery
721+
.select(sql`AVG(${sql.raw(field as string)}) as avg`)
561722
.executeTakeFirst()
562723

563-
// If we have an ID in the current instance, try to find the updated model
564-
if ((this as any).id) {
565-
return await this.applyFind((this as any).id)
724+
return result.avg || 0
725+
}
726+
727+
async avg(field: keyof C): Promise<number> {
728+
return await this.applyAvg(field)
729+
}
730+
731+
async applyChunk(size: number, callback: (models: T[]) => Promise<void>): Promise<void> {
732+
let page = 1
733+
let hasMore = true
734+
735+
while (hasMore) {
736+
// Get one batch
737+
const models = await this.selectFromQuery
738+
.selectAll()
739+
.limit(size)
740+
.offset((page - 1) * size)
741+
.execute()
742+
743+
// If we got fewer results than chunk size, this is the last batch
744+
if (models.length < size) {
745+
hasMore = false
746+
}
747+
748+
// Process this batch
749+
if (models.length > 0) {
750+
await callback(models)
751+
}
752+
753+
page++
566754
}
755+
}
567756

568-
return undefined
757+
async chunk(size: number, callback: (models: T[]) => Promise<void>): Promise<void> {
758+
await this.applyChunk(size, callback)
569759
}
570760

571761
// Methods to be implemented by child classes

0 commit comments

Comments
 (0)