增加虚拟列;
修复查询bug; UpdateBuilder和InsertBuilder增加自定义值
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yizhi/postgres",
|
"name": "@yizhi/postgres",
|
||||||
"version": "1.0.1",
|
"version": "1.0.4",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "typing/index.d.ts",
|
"types": "typing/index.d.ts",
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
|
101
src/entity.ts
101
src/entity.ts
@ -11,6 +11,8 @@ export interface IEntityFieldConfig {
|
|||||||
prop: string
|
prop: string
|
||||||
/** 是否是主键 */
|
/** 是否是主键 */
|
||||||
primary: boolean
|
primary: boolean
|
||||||
|
/** 是否是虚拟字段 */
|
||||||
|
virtual: boolean
|
||||||
/** 编码方式(存入数据库时) */
|
/** 编码方式(存入数据库时) */
|
||||||
encode: (data: any) => string
|
encode: (data: any) => string
|
||||||
/** 解码方式(查询数据库时) */
|
/** 解码方式(查询数据库时) */
|
||||||
@ -134,11 +136,12 @@ export function Table(table: string): Decorators.ClassDecorator<BasicEntity> {
|
|||||||
* @param encode 字段编码凡是
|
* @param encode 字段编码凡是
|
||||||
* @param decode 字段解码方式
|
* @param decode 字段解码方式
|
||||||
* @param primary 是否是主键
|
* @param primary 是否是主键
|
||||||
|
* @param virtual 是否是虚拟字段
|
||||||
*/
|
*/
|
||||||
export function Field(name: string | undefined, encode: IEntityFieldConfig["encode"], decode: IEntityFieldConfig["decode"], primary: boolean): Decorators.PropDecorator<BasicEntity> {
|
export function Field(name: string | undefined, encode: IEntityFieldConfig["encode"], decode: IEntityFieldConfig["decode"], primary: boolean, virtual: boolean): Decorators.PropDecorator<BasicEntity> {
|
||||||
return function (target, propertyKey) {
|
return function (target, propertyKey) {
|
||||||
const conf = entityConfigOf(target.constructor)
|
const conf = entityConfigOf(target.constructor)
|
||||||
conf.fields.push({ name: name ?? propertyKey, prop: propertyKey, encode, decode, primary });
|
conf.fields.push({ name: name ?? propertyKey, prop: propertyKey, encode, decode, primary, virtual });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,22 +151,32 @@ export function Field(name: string | undefined, encode: IEntityFieldConfig["enco
|
|||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
* @param option 字段选项
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function IntColumn(name: string, option?: { primary: boolean }): Decorators.PropDecorator<BasicEntity>
|
export function IntColumn(name: string, option?: { primary: boolean, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function IntColumn(option?: { primary: boolean }): Decorators.PropDecorator<BasicEntity>
|
export function IntColumn(option?: { primary: boolean, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function IntColumn(name?: any, option?: any) {
|
export function IntColumn(name?: any, option?: any) {
|
||||||
if (typeof name != "string") {
|
if (typeof name != "string") {
|
||||||
option = name;
|
option = name;
|
||||||
name = undefined;
|
name = undefined;
|
||||||
}
|
}
|
||||||
return Field(name, v => escapeValue(v), v => v, option?.primary ?? false);
|
return Field(name, v => escapeValue(v), v => v, option?.primary ?? false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义浮点数(包括定点小数)
|
* 定义浮点数(包括定点小数)
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function FloatColumn(name?: string) { return Field(name, v => escapeValue(v), v => v, false); }
|
export function FloatColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function FloatColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function FloatColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name !== "string") {
|
||||||
|
option = name;
|
||||||
|
name = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Field(name, v => escapeValue(v), v => v, false, option?.virtual ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,21 +184,29 @@ export function FloatColumn(name?: string) { return Field(name, v => escapeValue
|
|||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
* @param option 字段选项
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function StringColumn(name: string, option?: { primary?: boolean }): Decorators.PropDecorator<BasicEntity>
|
export function StringColumn(option?: { primary?: boolean, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function StringColumn(option?: { primary?: boolean }): Decorators.PropDecorator<BasicEntity>
|
export function StringColumn(name: string, option?: { primary?: boolean, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function StringColumn(name?: any, option?: any) {
|
export function StringColumn(name?: any, option?: any) {
|
||||||
if (typeof name != "string") {
|
if (typeof name != "string") {
|
||||||
option = name
|
option = name
|
||||||
name = undefined
|
name = undefined
|
||||||
}
|
}
|
||||||
return Field(name, v => escapeValue(v), v => v, option?.primary ?? false);
|
return Field(name, v => escapeValue(v), v => v, option?.primary ?? false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义布尔字段
|
* 定义布尔字段
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function BooleanColumn(name?: string) {
|
export function BooleanColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function BooleanColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function BooleanColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name != "string") {
|
||||||
|
option = name
|
||||||
|
name = undefined
|
||||||
|
}
|
||||||
|
|
||||||
const checker = (v: any) => {
|
const checker = (v: any) => {
|
||||||
if (typeof v == "boolean") return v;
|
if (typeof v == "boolean") return v;
|
||||||
if (typeof v != "string") return false;
|
if (typeof v != "string") return false;
|
||||||
@ -193,14 +214,22 @@ export function BooleanColumn(name?: string) {
|
|||||||
return ["true", "yes", "on"].includes(v)
|
return ["true", "yes", "on"].includes(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Field(name, (v) => escapeValue(checker(v)), checker, false);
|
return Field(name, (v) => escapeValue(checker(v)), checker, false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义日期字段
|
* 定义日期字段
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function DateColumn(name?: string) {
|
export function DateColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function DateColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function DateColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name != "string") {
|
||||||
|
option = name
|
||||||
|
name = undefined
|
||||||
|
}
|
||||||
|
|
||||||
return Field(name, v => {
|
return Field(name, v => {
|
||||||
if (v instanceof DateColumn) v = moment(v);
|
if (v instanceof DateColumn) v = moment(v);
|
||||||
if (typeof v == "string") v = moment(v);
|
if (typeof v == "string") v = moment(v);
|
||||||
@ -209,14 +238,21 @@ export function DateColumn(name?: string) {
|
|||||||
}, v => {
|
}, v => {
|
||||||
if (typeof v == "string" || (v instanceof global.Date)) return moment(v).format("YYYY-MM-DD");
|
if (typeof v == "string" || (v instanceof global.Date)) return moment(v).format("YYYY-MM-DD");
|
||||||
return null;
|
return null;
|
||||||
}, false);
|
}, false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义日期时间字段
|
* 定义日期时间字段
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
*/
|
*/
|
||||||
export function DateTimeColumn(name?: string) {
|
export function DateTimeColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function DateTimeColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function DateTimeColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name != "string") {
|
||||||
|
option = name
|
||||||
|
name = undefined
|
||||||
|
}
|
||||||
|
|
||||||
return Field(name, v => {
|
return Field(name, v => {
|
||||||
if (v instanceof DateColumn) v = moment(v);
|
if (v instanceof DateColumn) v = moment(v);
|
||||||
if (typeof v == "string") v = moment(v);
|
if (typeof v == "string") v = moment(v);
|
||||||
@ -225,12 +261,12 @@ export function DateTimeColumn(name?: string) {
|
|||||||
}, v => {
|
}, v => {
|
||||||
if (typeof v == "string" || (v instanceof global.Date)) return moment(v).format("YYYY-MM-DD HH:mm:ss");
|
if (typeof v == "string" || (v instanceof global.Date)) return moment(v).format("YYYY-MM-DD HH:mm:ss");
|
||||||
return null;
|
return null;
|
||||||
}, false);
|
}, false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//数组定义
|
//数组定义
|
||||||
function _Array<T>(name: string | undefined, typeName: string, itemEncoder: (v: any) => (string | null), itemDecoder: ((v: any) => (T | null)) | undefined) {
|
function _Array<T>(name: string | undefined, typeName: string, itemEncoder: (v: any) => (string | null), itemDecoder: ((v: any) => (T | null)) | undefined, virtual: boolean) {
|
||||||
return Field(name, v => {
|
return Field(name, v => {
|
||||||
if (!(v instanceof Array)) throw new DatabaseError("DB_ENTITY_FIELD_NOT_ARRAY", `字段${name}需要给定一个数组`);
|
if (!(v instanceof Array)) throw new DatabaseError("DB_ENTITY_FIELD_NOT_ARRAY", `字段${name}需要给定一个数组`);
|
||||||
return escapeValue(`{${v.map(item => itemEncoder(item)).join(",")}}`) + `::${typeName}`;
|
return escapeValue(`{${v.map(item => itemEncoder(item)).join(",")}}`) + `::${typeName}`;
|
||||||
@ -238,7 +274,7 @@ function _Array<T>(name: string | undefined, typeName: string, itemEncoder: (v:
|
|||||||
if (!(v instanceof Array)) return null;
|
if (!(v instanceof Array)) return null;
|
||||||
if (itemDecoder) return v.map(item => itemDecoder(item));
|
if (itemDecoder) return v.map(item => itemDecoder(item));
|
||||||
else return v;
|
else return v;
|
||||||
}, false);
|
}, false, virtual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,8 +282,8 @@ function _Array<T>(name: string | undefined, typeName: string, itemEncoder: (v:
|
|||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
* @param option 字段选项
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function IntArrayColumn(name: string, option?: { bytes?: 2 | 4 | 8 }): Decorators.PropDecorator<BasicEntity>
|
export function IntArrayColumn(name: string, option?: { bytes?: 2 | 4 | 8, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function IntArrayColumn(option?: { bytes?: 2 | 4 | 8 }): Decorators.PropDecorator<BasicEntity>
|
export function IntArrayColumn(option?: { bytes?: 2 | 4 | 8, virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
export function IntArrayColumn(name?: any, option?: any) {
|
export function IntArrayColumn(name?: any, option?: any) {
|
||||||
if (name && typeof name !== "string") {
|
if (name && typeof name !== "string") {
|
||||||
option = name
|
option = name
|
||||||
@ -262,26 +298,41 @@ export function IntArrayColumn(name?: any, option?: any) {
|
|||||||
item = parseInt(item);
|
item = parseInt(item);
|
||||||
if (isNaN(item) || !isFinite(item)) return null;
|
if (isNaN(item) || !isFinite(item)) return null;
|
||||||
return item;
|
return item;
|
||||||
});
|
}, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义字符串数组
|
* 定义字符串数组
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
|
* @param option 字段选项
|
||||||
*/
|
*/
|
||||||
export function StringArrayColumn(name?: string) {
|
export function StringArrayColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function StringArrayColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function StringArrayColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name != "string") {
|
||||||
|
option = name
|
||||||
|
name = undefined
|
||||||
|
}
|
||||||
|
|
||||||
return _Array(name, "varchar[]", item => {
|
return _Array(name, "varchar[]", item => {
|
||||||
if (typeof item == "string") return item;
|
if (typeof item == "string") return item;
|
||||||
else return null;
|
else return null;
|
||||||
}, item => item);
|
}, item => item, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定义jsonb字段
|
* 定义jsonb字段
|
||||||
* @param name 字段名称
|
* @param name 字段名称
|
||||||
*/
|
*/
|
||||||
export function JsonColumn(name?: string) {
|
export function JsonColumn(option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
return Field(name, v => escapeValue(JSON.stringify(v)) + "::jsonb", v => v, false);
|
export function JsonColumn(name: string, option?: { virtual?: boolean }): Decorators.PropDecorator<BasicEntity>
|
||||||
|
export function JsonColumn(name?: any, option?: any) {
|
||||||
|
if (typeof name != "string") {
|
||||||
|
option = name
|
||||||
|
name = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return Field(name, v => escapeValue(JSON.stringify(v)) + "::jsonb", v => v, false, option?.virtual ?? false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,7 +343,7 @@ export function TSVectorColumn(name?: string) {
|
|||||||
return Field(name, v => {
|
return Field(name, v => {
|
||||||
if (!(v instanceof TSVector)) throw new DatabaseError("DB_ENTITY_FIELD_NOT_TSVECTOR", `字段${name}需要给定一个TSVector`)
|
if (!(v instanceof TSVector)) throw new DatabaseError("DB_ENTITY_FIELD_NOT_TSVECTOR", `字段${name}需要给定一个TSVector`)
|
||||||
return `E'${v.value}'::tsvector`;
|
return `E'${v.value}'::tsvector`;
|
||||||
}, v => TSVector.fromValue(v), false);
|
}, v => TSVector.fromValue(v), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取实体配置
|
//获取实体配置
|
||||||
|
@ -5,8 +5,16 @@ export {
|
|||||||
TSVectorColumn,
|
TSVectorColumn,
|
||||||
JoinOne, JoinMany,
|
JoinOne, JoinMany,
|
||||||
BasicEntity,
|
BasicEntity,
|
||||||
|
IEntityConfig,
|
||||||
} from "./entity";
|
} from "./entity";
|
||||||
|
|
||||||
|
export {
|
||||||
|
IInsertBuilder,
|
||||||
|
IUpdateBuilder,
|
||||||
|
IDeleteBuilder,
|
||||||
|
ISelectBuilder,
|
||||||
|
} from "./query";
|
||||||
|
|
||||||
export * from "./type";
|
export * from "./type";
|
||||||
export * from "./database";
|
export * from "./database";
|
||||||
export * from "./error";
|
export * from "./error";
|
||||||
|
17
src/query.ts
17
src/query.ts
@ -23,6 +23,7 @@ interface IQueryFunc<R extends QueryResultRow = any> {
|
|||||||
(sql: string, args?: any[]): Promise<QueryResult<R>>
|
(sql: string, args?: any[]): Promise<QueryResult<R>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DataOrCustom<T> = T | (() => string);
|
||||||
|
|
||||||
type StringArgs<Str extends string, P extends string, S extends string> = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs<T, P, S>) : never;
|
type StringArgs<Str extends string, P extends string, S extends string> = Str extends `${string}${P}${infer R}${S}${infer T}` ? (R | StringArgs<T, P, S>) : never;
|
||||||
|
|
||||||
@ -189,7 +190,7 @@ export interface IInsertBuilder<E extends BasicEntity> extends IReturningFn<E> {
|
|||||||
* 设置要插入的数据
|
* 设置要插入的数据
|
||||||
* @param data 要插入的数据
|
* @param data 要插入的数据
|
||||||
*/
|
*/
|
||||||
data<Name extends BasicFieldName<E>>(data: Pick<E, Name> | Pick<E, Name>[]): this;
|
data<Name extends BasicFieldName<E>>(data: { [P in Name]: DataOrCustom<E[P]> } | Array<{ [P in Name]: DataOrCustom<E[P]> }>): this;
|
||||||
|
|
||||||
/** 执行插入 */
|
/** 执行插入 */
|
||||||
query(): Promise<E[]>
|
query(): Promise<E[]>
|
||||||
@ -200,7 +201,7 @@ export interface IUpdateBuilder<E extends BasicEntity> extends IWhereFn<E>, IRet
|
|||||||
* 设置更新数据
|
* 设置更新数据
|
||||||
* @param data 要更新的数据
|
* @param data 要更新的数据
|
||||||
*/
|
*/
|
||||||
set(data: Partial<Pick<E, BasicFieldName<E>>>): this
|
set(data: { [P in BasicFieldName<E>]?: DataOrCustom<E[P]> }): this
|
||||||
|
|
||||||
/** 执行更新 */
|
/** 执行更新 */
|
||||||
query(): Promise<E[]>
|
query(): Promise<E[]>
|
||||||
@ -435,7 +436,7 @@ function buildCondition<E extends BasicEntity>(builder: SQLBuilder<E>, option: C
|
|||||||
wheres.push(`${fieldName}<${field.encode(opv)}`);
|
wheres.push(`${fieldName}<${field.encode(opv)}`);
|
||||||
break;
|
break;
|
||||||
case "lte":
|
case "lte":
|
||||||
wheres.push(`${fieldName}<=${field.encode(topVal)}`);
|
wheres.push(`${fieldName}<=${field.encode(opv)}`);
|
||||||
break;
|
break;
|
||||||
case "like":
|
case "like":
|
||||||
wheres.push(`${fieldName} like ${escapeValue(opv)}`);
|
wheres.push(`${fieldName} like ${escapeValue(opv)}`);
|
||||||
@ -754,7 +755,7 @@ export class InsertBuilder<E extends BasicEntity> extends WithReturning(SQLBuild
|
|||||||
this.#query = query;
|
this.#query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public data<Name extends BasicFieldName<E>>(data: Pick<E, Name> | Pick<E, Name>[]) {
|
public data<Name extends BasicFieldName<E>>(data: { [P in Name]: DataOrCustom<E[P]> } | Array<{ [P in Name]: DataOrCustom<E[P]> }>) {
|
||||||
data = (data instanceof Array) ? data : [data];
|
data = (data instanceof Array) ? data : [data];
|
||||||
this.#values = [];
|
this.#values = [];
|
||||||
this.#columns = [];
|
this.#columns = [];
|
||||||
@ -763,9 +764,11 @@ export class InsertBuilder<E extends BasicEntity> extends WithReturning(SQLBuild
|
|||||||
for (const item of data) {
|
for (const item of data) {
|
||||||
const buffer: (string | null)[] = [];
|
const buffer: (string | null)[] = [];
|
||||||
for (const col of this.config.fields) {
|
for (const col of this.config.fields) {
|
||||||
|
if (col.virtual) continue;
|
||||||
if (!(col.prop in item)) continue;
|
if (!(col.prop in item)) continue;
|
||||||
const val = item[col.prop as keyof typeof item];
|
const val = item[col.prop as keyof typeof item];
|
||||||
if (val === null || val === undefined) buffer.push("null");
|
if (val === null || val === undefined) buffer.push("null");
|
||||||
|
else if (typeof val === "function") buffer.push(val());
|
||||||
else buffer.push(col.encode(val));
|
else buffer.push(col.encode(val));
|
||||||
if (!columnsSet) this.#columns.push(escapeID(col.name));
|
if (!columnsSet) this.#columns.push(escapeID(col.name));
|
||||||
}
|
}
|
||||||
@ -801,12 +804,14 @@ export class UpdateBuilder<E extends BasicEntity> extends WithReturning(WithWher
|
|||||||
this.#query = query;
|
this.#query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(data: Partial<Pick<E, BasicFieldName<E>>>) {
|
public set(data: { [P in BasicFieldName<E>]?: DataOrCustom<E[P]> }) {
|
||||||
this.#updates = []
|
this.#updates = []
|
||||||
for (const col of this.config.fields) {
|
for (const col of this.config.fields) {
|
||||||
|
if (col.virtual) continue;
|
||||||
const val = data[col.prop as keyof typeof data];
|
const val = data[col.prop as keyof typeof data];
|
||||||
if (val === undefined) continue;
|
if (val === undefined) continue;
|
||||||
if (val === null) this.#updates.push(`${escapeID(col.name)}=null`);
|
else if (val === null) this.#updates.push(`${escapeID(col.name)}=null`);
|
||||||
|
else if (typeof val === "function") this.#updates.push(`${escapeID(col.name)}=${val()}`);
|
||||||
else this.#updates.push(`${escapeID(col.name)}=${col.encode(val)}`);
|
else this.#updates.push(`${escapeID(col.name)}=${col.encode(val)}`);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
Reference in New Issue
Block a user