Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: fix(typing/Predicate): 修复 Predicate 类型 #57

Open
wants to merge 1 commit into
base: chore/upgrade-ts
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions src/interface/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export type DeepPartial<T> = {
[K in keyof T]?: Partial<T[K]>
}

export type RelationshipPredicate<T> = {
[P in keyof T]?: lf.schema.Column
}

export interface SchemaMetadata<T> {
type: RDBType | Relationship
primaryKey?: boolean
Expand All @@ -17,7 +21,7 @@ export interface SchemaMetadata<T> {
*/
virtual?: {
name: string
where<U>(ref: TableShape<U>): Predicate<T>
where<U>(ref: TableShape<U>): RelationshipPredicate<T>
}
}

Expand All @@ -26,7 +30,7 @@ export type TableShape<T> = lf.schema.Table & {
}

export type SchemaDef<T> = {
[P in keyof T]: SchemaMetadata<T[P]>
[P in keyof T]: SchemaMetadata<T>
} & {
dispose?: SchemaDisposeFunction<T>
['@@dispose']?: SchemaDisposeFunction<T>
Expand Down Expand Up @@ -170,9 +174,14 @@ export interface PredicateMeta<T> {
$isNotNull: boolean
}

export type Predicate<T> = {
[P in keyof T & PredicateMeta<T>]?: Partial<PredicateMeta<T>> | ValueLiteral | Predicate<T[P]>
}
export type Predicate<T> = Partial<{
[P in keyof T]:
| Partial<PredicateMeta<T[P]>> // 操作符表达式,如 { $or: [{ $gt: 1 }, { $lt: 5 }] }
| ValueLiteral // 字面量值
| String // 一种特殊的字面量值类型,常用 interface X extends String { kind?: 'x' } 表达
}
& PredicateMeta<T>
>

export { StatementType, JoinMode, LeafType, Relationship, DataStoreType, RDBType }

Expand Down
22 changes: 12 additions & 10 deletions src/storage/Database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,14 @@ export class Database {
return this.database$.pipe(concatMap(insert))
}

get<T>(tableName: string, query: Query<T> = {}, mode: JoinMode = JoinMode.imlicit): QueryToken<T> {
get<T = any>(tableName: string, query: Query<T> = {}, mode: JoinMode = JoinMode.imlicit): QueryToken<T> {
const selector$ = this.database$.pipe(
map(db => this.buildSelector(db, tableName, query, mode))
)
return new QueryToken<T>(selector$)
}

update<T>(tableName: string, clause: Predicate<T>, raw: Partial<T>): Observable<ExecutorResult> {
update<T = any>(tableName: string, clause: Predicate<T>, raw: Partial<T>): Observable<ExecutorResult> {
const type = getType(raw)
if (type !== 'Object') {
return Observable.throw(Exception.InvalidType(['Object', type]))
Expand Down Expand Up @@ -226,7 +226,7 @@ export class Database {
return this.database$.pipe(concatMap(update))
}

delete<T>(tableName: string, clause: Predicate<T> = {}): Observable<ExecutorResult> {
delete<T = any>(tableName: string, clause: Predicate<T> = {}): Observable<ExecutorResult> {
const [ pk, err ] = tryCatch<string>(this.findPrimaryKey)(tableName)
if (err) {
return Observable.throw(err)
Expand Down Expand Up @@ -266,13 +266,13 @@ export class Database {
return this.database$.pipe(concatMap(deletion))
}

upsert<T>(tableName: string, raw: T): Observable<ExecutorResult>
upsert<T = any>(tableName: string, raw: T): Observable<ExecutorResult>

upsert<T>(tableName: string, raw: T[]): Observable<ExecutorResult>
upsert<T = any>(tableName: string, raw: T[]): Observable<ExecutorResult>

upsert<T>(tableName: string, raw: T | T[]): Observable<ExecutorResult>
upsert<T = any>(tableName: string, raw: T | T[]): Observable<ExecutorResult>

upsert<T>(tableName: string, raw: T | T[]): Observable<ExecutorResult> {
upsert<T = any>(tableName: string, raw: T | T[]): Observable<ExecutorResult> {
const upsert = (db: lf.Database) => {
const sharing = new Map<any, Mutation>()
const insert: Mutation[] = []
Expand All @@ -297,7 +297,7 @@ export class Database {
return this.database$.pipe(concatMap(upsert))
}

remove<T>(tableName: string, clause: Clause<T> = {}): Observable<ExecutorResult> {
remove<T = any>(tableName: string, clause: Clause<T> = {}): Observable<ExecutorResult> {
const [schema, err] = tryCatch<ParsedSchema>(this.findSchema)(tableName)
if (err) {
return Observable.throw(err)
Expand Down Expand Up @@ -571,7 +571,7 @@ export class Database {
mainTable: table!
}
const { limit, skip } = clause
const provider = new PredicateProvider(table!, clause.where)
const provider = new PredicateProvider<T>(table!, clause.where)

return new Selector<T>(db, query, matcher, provider, limit, skip, orderDesc)
}
Expand Down Expand Up @@ -852,7 +852,9 @@ export class Database {
entities.forEach(entity => {
const pkVal = entity[pk]
const clause = createPkClause(pk, pkVal)
const predicate = createPredicate(table, clause)
// todo(dingwen): 调查是该用 clause 还是 clause.where,
// 可以确定的是,clause 是带 where 的。
const predicate = createPredicate(table, clause.where)
const query = predicatableQuery(db, table, predicate!, StatementType.Delete)

queryCollection.push(query)
Expand Down
18 changes: 13 additions & 5 deletions src/storage/modules/PredicateProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const predicateFactory = {
},
}

type Pred = typeof predicateFactory

const compoundPredicateFactory = {
$and (predicates: lf.Predicate[]): lf.Predicate {
return lf.op.and(...predicates)
Expand All @@ -67,7 +69,9 @@ const compoundPredicateFactory = {
},
}

export class PredicateProvider<T> {
type CompPred = typeof compoundPredicateFactory

export class PredicateProvider<T = any> {

constructor(
private table: lf.schema.Table,
Expand All @@ -93,8 +97,12 @@ export class PredicateProvider<T> {
}

private normalizeMeta(meta: Predicate<T>, column?: lf.schema.Column): lf.Predicate[] {
const buildSinglePred = (col: lf.schema.Column, val: any, key: string): lf.Predicate =>
this.checkMethod(key) ? predicateFactory[key](col, val) : col.eq(val as ValueLiteral)
const buildSinglePred = <K extends string>(
col: lf.schema.Column,
val: K extends (keyof Pred) ? Pred[K] : ValueLiteral,
key: K
): lf.Predicate =>
this.checkMethod(key) ? predicateFactory[key](col, val) : col.eq(val)

const predicates: lf.Predicate[] = []

Expand Down Expand Up @@ -124,11 +132,11 @@ export class PredicateProvider<T> {
return predicates
}

private checkMethod(methodName: string) {
private checkMethod(methodName: string): methodName is keyof Pred {
return typeof predicateFactory[methodName] === 'function'
}

private checkCompound(methodName: string) {
private checkCompound(methodName: string): methodName is keyof CompPred {
return typeof compoundPredicateFactory[methodName] === 'function'
}

Expand Down
4 changes: 2 additions & 2 deletions test/schemas/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export interface ProjectSchema {
_id: TeambitionTypes.ProjectId
name: string
isArchived: boolean
posts: any[]
posts: any
}
export default (db: Database) => db.defineSchema<ProjectSchema>('Project', {
_id: {
Expand All @@ -21,7 +21,7 @@ export default (db: Database) => db.defineSchema<ProjectSchema>('Project', {
type: Relationship.oneToMany,
virtual: {
name: 'Post',
where: ref => ({
where: (ref) => ({
_id: ref.belongTo
})
}
Expand Down
57 changes: 1 addition & 56 deletions test/schemas/Test.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,11 @@
import { TeambitionTypes, Database, RDBType, Relationship } from '../index'
import { TeambitionTypes, Database, RDBType } from '../index'

export interface TestSchema {
_id: string
name: string
taskId: TeambitionTypes.TaskId
}

export const TestFixture = (db: Database) => {
const schema = {
_id: {
type: RDBType.STRING,
primaryKey: true
},
data1: {
type: RDBType.ARRAY_BUFFER,
},
data2: {
type: RDBType.NUMBER
},
data3: {
type: RDBType.STRING,
virtual: {
name: 'Project',
where: (ref: any) => ({
_projectId: ref._id
})
}
},
data4: {
type: RDBType.OBJECT
},
data5: {
type: RDBType.INTEGER
}
}

db.defineSchema('Fixture1', schema)
}

export const TestFixture2 = (db: Database) => {
const schema = {
_id: {
Expand Down Expand Up @@ -67,26 +35,3 @@ export const TestFixture2 = (db: Database) => {

return db.defineSchema('Fixture2', schema)
}

export const TestFixture3 = (db: Database) => {
const schema = {
id: {
type: RDBType.STRING,
primaryKey: true
},
data1: {
type: RDBType.NUMBER
},
data2: {
type: Relationship.oneToMany,
virtual: {
name: 'Project',
where: (ref: any) => ({
id: ref['_id']
})
}
}
}

return db.defineSchema('Test', schema)
}
2 changes: 1 addition & 1 deletion test/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export { TaskSchema } from './Task'
export { ProgramSchema } from './Program'
export { ModuleSchema } from './Module'
export { EngineerSchema } from './Engineer'
export { TestSchema, TestFixture, TestFixture2 } from './Test'
export { TestSchema, TestFixture2 } from './Test'

/**
* import ActivitySelectMeta from './Activity'
Expand Down
21 changes: 9 additions & 12 deletions test/specs/storage/Database.public.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ export default describe('Database Testcase: ', () => {

it('shouldn\'t to update column which is defined as primarykey', function* () {
const note = 'foo'
yield database.update('Task', target._id as string, {
yield database.update('Task', target._id as any, {
_id: 'bar',
note
})
Expand Down Expand Up @@ -652,7 +652,7 @@ export default describe('Database Testcase: ', () => {
const u1 = uuid()
const u2 = uuid()

yield database.update('Task', clause, {
yield database.update<TaskSchema>('Task', clause, {
_stageId: u1
})

Expand All @@ -662,7 +662,7 @@ export default describe('Database Testcase: ', () => {
}
}).values()

yield database.update('Task', clause, {
yield database.update<TaskSchema>('Task', clause, {
_stageId: u2
})

Expand All @@ -678,7 +678,7 @@ export default describe('Database Testcase: ', () => {

it('should be able to update property which is stored as hidden column', function* () {
const newCreated = new Date(2017, 1, 1)
yield database.update('Task', {
yield database.update<TaskSchema>('Task', {
_id: target._id
}, {
created: newCreated.toISOString()
Expand Down Expand Up @@ -760,7 +760,7 @@ export default describe('Database Testcase: ', () => {
const errSpy = sinon.spy((): void => void 0)

tmpDB.connect()
tmpDB.update(T, { id: 1 }, {
tmpDB.update(T, { id: 1 } as any, {
members: ['1', '2']
})
.catch(errSpy)
Expand Down Expand Up @@ -1048,11 +1048,8 @@ export default describe('Database Testcase: ', () => {

const execRet1 = yield database.upsert<ProgramSchema>('Program', program)

yield database.delete<ProgramSchema>('Program', {
where: {
_id: program._id
}
})
// todo(dingwen): delete 的参数究竟需不需要 where
yield database.delete<ProgramSchema>('Program', { _id: program._id })

const execRet2 = yield database.upsert<ProgramSchema>('Program', program)

Expand Down Expand Up @@ -1164,7 +1161,7 @@ export default describe('Database Testcase: ', () => {
const moduleCount = 20

const programs = programGen(programCount, moduleCount)
const engineerIds = Array.from(new Set(
const engineerIds: string[] = Array.from(new Set(
programs
.map(p => p.modules)
.reduce((acc, pre) => acc.concat(pre))
Expand All @@ -1173,7 +1170,7 @@ export default describe('Database Testcase: ', () => {
)
yield database.upsert('Program', programs)

const clause = { where: { $in: engineerIds } }
const clause = { where: { _id: { $in: engineerIds } } }
const storedEngineers = yield database.get('Engineer', clause).values()

const execRet = yield database.remove('Module')
Expand Down