Skip to content

Commit

Permalink
feat(orm): refactor typings
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Aug 5, 2021
1 parent 48f8a93 commit 30a7319
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 52 deletions.
46 changes: 25 additions & 21 deletions packages/koishi-core/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,16 @@ export namespace Query {
return query as any
}

export interface Options<T> {
export interface Options<T extends string> {
limit?: number
offset?: number
select?: T[]
fields?: T[]
}

export type Modifier<T> = T[] | Options<T>
export type Modifier<T extends string = any> = T[] | Options<T>

export function resolveModifier<T>(modifier: Modifier<T>): Options<T> {
if (Array.isArray(modifier)) return { select: modifier }
export function resolveModifier<T extends string>(modifier: Modifier<T>): Options<T> {
if (Array.isArray(modifier)) return { fields: modifier }
return modifier || {}
}

Expand All @@ -90,6 +90,8 @@ export namespace Query {
}
}

type MaybeArray<T> = T | readonly T[]

export interface User extends Record<Platform, string> {
id: string
flag: number
Expand Down Expand Up @@ -130,6 +132,14 @@ export namespace User {
}
return result as User
}

export interface Datbase {
getUser<K extends Field, T extends Index>(type: T, id: string, modifier?: Query.Modifier<K>): Promise<Pick<User, K | T>>
getUser<K extends Field, T extends Index>(type: T, ids: readonly string[], modifier?: Query.Modifier<K>): Promise<Pick<User, K>[]>
getUser<K extends Field, T extends Index>(type: T, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>
setUser<T extends Index>(type: T, id: string, data: Partial<User>): Promise<void>
createUser<T extends Index>(type: T, id: string, data: Partial<User>): Promise<void>
}
}

export interface Channel {
Expand Down Expand Up @@ -165,25 +175,19 @@ export namespace Channel {
}

extend((type, id) => ({ id: `${type}:${id}`, flag: 0, disable: [] }))
}

type MaybeArray<T> = T | readonly T[]

export interface Database extends Query.Database {
getUser<K extends User.Field, T extends User.Index>(type: T, id: string, modifier?: Query.Modifier<K>): Promise<Pick<User, K | T>>
getUser<K extends User.Field, T extends User.Index>(type: T, ids: readonly string[], modifier?: Query.Modifier<K>): Promise<Pick<User, K>[]>
getUser<K extends User.Field, T extends User.Index>(type: T, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>
setUser<T extends User.Index>(type: T, id: string, data: Partial<User>): Promise<void>
createUser<T extends User.Index>(type: T, id: string, data: Partial<User>): Promise<void>

getChannel<K extends Channel.Field>(type: Platform, id: string, modifier?: Query.Modifier<K>): Promise<Pick<Channel, K | 'id'>>
getChannel<K extends Channel.Field>(type: Platform, ids: readonly string[], modifier?: Query.Modifier<K>): Promise<Pick<Channel, K>[]>
getChannel<K extends Channel.Field>(type: Platform, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>
getAssignedChannels<K extends Channel.Field>(fields?: K[], assignMap?: Record<string, readonly string[]>): Promise<Pick<Channel, K>[]>
setChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>
createChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>
export interface Database {
getChannel<K extends Field>(type: Platform, id: string, modifier?: Query.Modifier<K>): Promise<Pick<Channel, K | 'id'>>
getChannel<K extends Field>(type: Platform, ids: readonly string[], modifier?: Query.Modifier<K>): Promise<Pick<Channel, K>[]>
getChannel<K extends Field>(type: Platform, id: MaybeArray<string>, modifier?: Query.Modifier<K>): Promise<any>
getAssignedChannels<K extends Field>(fields?: K[], assignMap?: Record<string, readonly string[]>): Promise<Pick<Channel, K>[]>
setChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>
createChannel(type: Platform, id: string, data: Partial<Channel>): Promise<void>
}
}

export interface Database extends Query.Database, User.Datbase, Channel.Database {}

type Methods<S, T> = {
[K in keyof S]?: S[K] extends (...args: infer R) => infer U ? (this: T, ...args: R) => U : S[K]
}
Expand Down
19 changes: 8 additions & 11 deletions packages/koishi-test-utils/src/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@ const queryOperators: ([string, (data: any, value: any) => boolean])[] = Object.
$lte: (data, value) => value <= data,
})

function applyModifier(data: any[], modifier: Query.Modifier<any>) {
const { select, limit = Infinity, offset = 0 } = Query.resolveModifier(modifier)
return data
.map(row => clone(pick(row, select)))
.slice(offset, offset + limit)
}

Database.extend(MemoryDatabase, {
async get(name, query, modifier) {
function executeQuery(query: Query.Expr, data: any): boolean {
Expand All @@ -92,7 +85,11 @@ Database.extend(MemoryDatabase, {
}

const expr = Query.resolve(name, query)
return applyModifier(this.$table(name).filter(row => executeQuery(expr, row)), modifier)
const { fields, limit = Infinity, offset = 0 } = Query.resolveModifier(modifier)
return this.$table(name)
.filter(row => executeQuery(expr, row))
.map(row => clone(pick(row, fields)))
.slice(offset, offset + limit)
},

async remove(name, query) {
Expand Down Expand Up @@ -158,11 +155,11 @@ Database.extend(MemoryDatabase, {
}
},

async getAssignedChannels(modifier, assignMap = this.app.getSelfIds()) {
return applyModifier(this.$table('channel').filter((row) => {
async getAssignedChannels(fields, assignMap = this.app.getSelfIds()) {
return this.$table('channel').filter((row) => {
const [type] = row.id.split(':')
return assignMap[type]?.includes(row.assignee)
}), modifier)
}).map(row => clone(pick(row, fields)))
},

async setChannel(type, id, data) {
Expand Down
20 changes: 7 additions & 13 deletions packages/plugin-mongo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ declare module 'koishi-core' {
}
}

function projection(keys: Iterable<string>) {
const d = {}
for (const key of keys) d[key] = 1
return d
}

function escapeKey<T extends Partial<User>>(doc: T) {
const data: T = { ...doc }
delete data.timers
Expand Down Expand Up @@ -127,8 +121,8 @@ Database.extend(MongoDatabase, {
const filter = createFilter(name, query)
if (!filter) return []
let cursor = this.db.collection(name).find(filter)
const { select, limit, offset = 0 } = Query.resolveModifier(modifier)
if (select) cursor = cursor.project(projection(select))
const { fields, limit, offset = 0 } = Query.resolveModifier(modifier)
if (fields) cursor = cursor.project(Object.fromEntries(fields.map(key => [key, 1])))
if (offset) cursor = cursor.skip(offset)
if (limit) cursor = cursor.limit(offset + limit)
const data = await cursor.toArray()
Expand Down Expand Up @@ -169,9 +163,9 @@ Database.extend(MongoDatabase, {
},

async getUser(type, id, modifier) {
const { select } = Query.resolveModifier(modifier)
const { fields } = Query.resolveModifier(modifier)
const applyDefault = (user: User) => ({
...pick(User.create(type, user[type]), select),
...pick(User.create(type, user[type]), fields),
...unescapeKey(user),
})

Expand All @@ -197,7 +191,7 @@ Database.extend(MongoDatabase, {

async getChannel(type, pid, modifier) {
modifier = Query.resolveModifier(modifier)
const fields = modifier.select.slice()
const fields = modifier.fields.slice()
const applyDefault = (channel: Channel) => ({
...pick(Channel.create(type, channel.pid), fields),
...omit(channel, ['type', 'pid']),
Expand All @@ -207,13 +201,13 @@ Database.extend(MongoDatabase, {
if (Array.isArray(pid)) {
const ids = pid.map(id => `${type}:${id}`)
if (fields && !fields.length) return ids.map(id => ({ id }))
if (index >= 0) modifier.select.splice(index, 1, 'type', 'pid')
if (index >= 0) modifier.fields.splice(index, 1, 'type', 'pid')
const data = await this.get('channel', ids, modifier)
return data.map(applyDefault)
} else {
const id = `${type}:${pid}`
if (fields && !fields.length) return { id }
if (index >= 0) modifier.select.splice(index, 1)
if (index >= 0) modifier.fields.splice(index, 1)
const data = await this.get('channel', id, modifier)
return data[0] && { ...applyDefault(data[0]), id }
}
Expand Down
14 changes: 7 additions & 7 deletions packages/plugin-mysql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ Database.extend(MysqlDatabase, {
async get(name, query, modifier) {
const filter = createFilter(name, query)
if (filter === '0') return []
const { select, limit, offset } = Query.resolveModifier(modifier)
const fields = this.joinKeys(this.inferFields(name, select))
let sql = `SELECT ${fields} FROM ${name} WHERE ${filter}`
const { fields, limit, offset } = Query.resolveModifier(modifier)
const keys = this.joinKeys(this.inferFields(name, fields))
let sql = `SELECT ${keys} FROM ${name} WHERE ${filter}`
if (limit) sql += ' LIMIT ' + limit
if (offset) sql += ' OFFSET ' + offset
return this.query(sql)
Expand Down Expand Up @@ -130,8 +130,8 @@ Database.extend(MysqlDatabase, {
},

async getUser(type, id, modifier) {
const { select } = Query.resolveModifier(modifier)
if (select && !select.length) {
const { fields } = Query.resolveModifier(modifier)
if (fields && !fields.length) {
return Array.isArray(id) ? id.map(id => ({ [type]: id })) : { [type]: id }
}
const data = await this.get('user', { [type]: id }, modifier)
Expand Down Expand Up @@ -165,8 +165,8 @@ Database.extend(MysqlDatabase, {
},

async getChannel(type, pid, modifier) {
const { select } = Query.resolveModifier(modifier)
if (select && !select.length) {
const { fields } = Query.resolveModifier(modifier)
if (fields && !fields.length) {
return Array.isArray(pid) ? pid.map(id => ({ id: `${type}:${id}` })) : { id: `${type}:${pid}` }
}
const id = Array.isArray(pid) ? pid.map(id => `${type}:${id}`) : `${type}:${pid}`
Expand Down

0 comments on commit 30a7319

Please sign in to comment.