From dbeb55a596b253f99de558bc6377a4fecfba5d3d Mon Sep 17 00:00:00 2001 From: maslow Date: Sat, 16 Oct 2021 20:47:08 +0800 Subject: [PATCH] feat(db-ql): refactor query & document api impl; deprecate joins api; deprecate merge(); --- packages/database-ql/src/aggregate.ts | 5 +- packages/database-ql/src/collection.ts | 2 +- packages/database-ql/src/constant.ts | 30 +- packages/database-ql/src/document.ts | 276 +++--------- packages/database-ql/src/interface.ts | 31 +- packages/database-ql/src/query.ts | 581 +++++++++---------------- 6 files changed, 314 insertions(+), 611 deletions(-) diff --git a/packages/database-ql/src/aggregate.ts b/packages/database-ql/src/aggregate.ts index 63c62001b6..e90aa72a53 100644 --- a/packages/database-ql/src/aggregate.ts +++ b/packages/database-ql/src/aggregate.ts @@ -1,3 +1,4 @@ +import { ActionType } from './constant' import { Db } from './index' import { RequestInterface } from './interface' import { GetRes } from './result-types' @@ -22,9 +23,9 @@ export default class Aggregation { if (!this._collectionName || !this._db) { throw new Error('Aggregation pipeline cannot send request') } - const result = await this._request.send('database.aggregate', { + const result = await this._request.send(ActionType.aggregate, { collectionName: this._collectionName, - stages: this._stages + // stages: this._stages }) if (result && result.data && result.data.list) { return { diff --git a/packages/database-ql/src/collection.ts b/packages/database-ql/src/collection.ts index 8080f4a523..1b3cadb5c8 100644 --- a/packages/database-ql/src/collection.ts +++ b/packages/database-ql/src/collection.ts @@ -31,7 +31,7 @@ export class CollectionReference extends Query { /** * 获取文档的引用 * - * @param docID - 文档ID + * @param docID - 文档 ID */ doc(docID: string | number): DocumentReference { if (!docID) { diff --git a/packages/database-ql/src/constant.ts b/packages/database-ql/src/constant.ts index 643df24e16..97f59a5d9a 100644 --- a/packages/database-ql/src/constant.ts +++ b/packages/database-ql/src/constant.ts @@ -64,7 +64,7 @@ const WhereFilterOpList = ['<', '<=', '==', '>=', '>'] /** * 操作符别名 */ -enum Opeartor { +enum Operator { lt = '<', gt = '>', lte = '<=', @@ -77,11 +77,11 @@ enum Opeartor { * SDK => MongoDB */ const OperatorMap = { - [Opeartor.eq]: '$eq', - [Opeartor.lt]: '$lt', - [Opeartor.lte]: '$lte', - [Opeartor.gt]: '$gt', - [Opeartor.gte]: '$gte' + [Operator.eq]: '$eq', + [Operator.lt]: '$lt', + [Operator.lte]: '$lte', + [Operator.gt]: '$gt', + [Operator.gte]: '$gte' } const UpdateOperatorList = [ @@ -98,13 +98,13 @@ const UpdateOperatorList = [ '$position' ] -/** - * queryType - */ - -enum QueryType { - WHERE = 'WHERE', - DOC = 'DOC' +enum ActionType { + add = 'database.addDocument', + query = 'database.queryDocument', + update = 'database.updateDocument', + count = 'database.countDocument', + remove = 'database.deleteDocument', + aggregate = 'database.aggregate' } export { @@ -112,10 +112,10 @@ export { FieldType, WhereFilterOp, WhereFilterOpList, - Opeartor, + Operator, OperatorMap, OrderByDirection, OrderDirectionList, UpdateOperatorList, - QueryType + ActionType } diff --git a/packages/database-ql/src/document.ts b/packages/database-ql/src/document.ts index 94d84f4a91..c78deddbf2 100644 --- a/packages/database-ql/src/document.ts +++ b/packages/database-ql/src/document.ts @@ -1,100 +1,67 @@ import { Db } from './index' -import { UpdateSerializer } from './serializer/update' import { serialize } from './serializer/datatype' import { UpdateCommand } from './commands/update' -import { QueryType } from './constant' +import { ActionType } from './constant' import { AddRes, GetOneRes, RemoveRes, UpdateRes } from './result-types' -import { RequestInterface } from './interface' -import { EJSON, ObjectId } from 'bson' -import { isObjectId } from './utils/type' -// import { Util } from './util' +import { ProjectionType } from './interface' +import { Query } from './query' /** - * 文档模块 - * + * Db document */ export class DocumentReference { /** - * 文档ID + * document id */ - readonly id: string | number | ObjectId + readonly id: any /** - * + * query object */ - readonly projection: Object + private _query: Query /** - * 数据库引用 - * - * @internal + * db reference */ private _db: Db - get primaryKey(): string { - return this._db.primaryKey - } - /** - * 集合名称 - * - * @internal + * collection name */ readonly _coll: string /** - * Request 实例 - * - * @internal + * @param db - db ref + * @param coll - collection + * @param docID - document id */ - private request: RequestInterface - - /** - * 初始化 - * - * @internal - * - * @param db - 数据库的引用 - * @param coll - 集合名称 - * @param docID - 文档ID - */ - constructor(db: Db, coll: string, docID: string | number | ObjectId, projection = {}) { + constructor(db: Db, coll: string, docID: any, query?: Query) { this._db = db this._coll = coll - this.id = isObjectId(docID) ? EJSON.serialize(docID) as ObjectId : docID - /* eslint-disable new-cap*/ - this.request = this._db.request - this.projection = projection + this.id = docID + this._query = query || new Query(db, coll) } /** * 创建一篇文档 * - * @param data - 文档数据 - * @internal + * @param data - document data */ async create(data: any, options?: { multi: boolean }): Promise { - if (!options) { - options = { multi: false } - } else { - options.multi = options.multi ?? false + if (!data || typeof data !== 'object' || 0 === Object.keys(data)?.length) { + throw new Error('data cannot be empty object') } - let params = { + const params = { collectionName: this._coll, - // data: Util.encodeDocumentDataForReq(data, false, false) data: serialize(data), - multi: options.multi + multi: options?.multi ?? false } - if (this.id) { - params[`${this.primaryKey}`] = this.id - } - - const res = await this.request - .send('database.addDocument', params) + const res = await this._query + .send(ActionType.add, params) if (res.error) { return { @@ -108,7 +75,7 @@ export class DocumentReference { } return { - id: res.data._id || res.data[this.primaryKey], + id: res.data._id || res.data[this._db.primaryKey], insertedCount: res.data.insertedCount, requestId: res.requestId, ok: true @@ -118,35 +85,17 @@ export class DocumentReference { /** * 创建或添加数据 * - * 如果文档ID不存在,则创建该文档并插入数据,根据返回数据的 upsertId 判断 - * 添加数据的话,根据返回数据的 set 判断影响的行数 + * 如果该文档 ID 在数据库中不存在,则创建该文档并插入数据,根据返回数据的 upsertId 判断 * - * @param data - 文档数据 + * @param data - document data */ async set(data: Object): Promise { if (!this.id) { - return { - code: 'INVALID_PARAM', - error: 'docId 不能为空' - } - } - - if (!data || typeof data !== 'object') { - return { - code: 'INVALID_PARAM', - error: '参数必需是非空对象' - } - } - - if (data.hasOwnProperty('_id')) { - return { - code: 'INVALID_PARAM', - error: '不能更新 _id 的值' - } + throw new Error('document id cannot be empty') } let hasOperator = false - const checkMixed = objs => { + const checkMixed = (objs: any) => { if (typeof objs === 'object') { for (let key in objs) { if (objs[key] instanceof UpdateCommand) { @@ -160,46 +109,16 @@ export class DocumentReference { checkMixed(data) if (hasOperator) { - //不能包含操作符 - return Promise.resolve({ - code: 'DATABASE_REQUEST_FAILED', - error: 'update operator complicit' - }) + // 不能包含操作符 + throw new Error('data cannot contain operator') } - const merge = false //data不能带有操作符 - let param = { - collectionName: this._coll, - queryType: QueryType.DOC, - // data: Util.encodeDocumentDataForReq(data, merge, false), - data: serialize(data), - multi: false, - merge, - upsert: true - } - - if (this.id) { - param['query'] = { [this.primaryKey]: this.id } - } - - const res = await this.request - .send('database.updateDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - ok: false, - code: res.code - } - } - - return { - updated: res.data.updated, - matched: res.data.matched, - upsertId: res.data.upserted_id, - requestId: res.requestId, - ok: true - } + // merge === false indicates replace operation + const merge = false + const res = await this._query + .where({ [this._db.primaryKey]: this.id }) + .update(serialize(data), { merge, multi: false, upsert: true }) + return res } /** @@ -208,125 +127,42 @@ export class DocumentReference { * @param data - 文档数据 */ async update(data: Object): Promise { - - if (!data || typeof data !== 'object') { - return { - code: 'INVALID_PARAM', - error: '参数必需是非空对象' - } - } - - if (data.hasOwnProperty('_id')) { - return { - code: 'INVALID_PARAM', - error: '不能更新 _id 的值' - } - } - - const query = { [this.primaryKey]: this.id } - const merge = true //把所有更新数据转为带操作符的 - const param = { - collectionName: this._coll, - // data: Util.encodeDocumentDataForReq(data, merge, true), - data: UpdateSerializer.encode(data), - query: query, - queryType: QueryType.DOC, - multi: false, - merge, - upsert: false - } - - const res = await this.request.send('database.updateDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - ok: false, - code: res.code - } - } - - return { - updated: res.data.updated, - matched: res.data.matched, - upsertId: res.data.upserted_id, - requestId: res.requestId, - ok: true - } + // 把所有更新数据转为带操作符的 + const merge = true + const options = { merge, multi: false, upsert: false } + + const res = await this._query + .where({ [this._db.primaryKey]: this.id }) + .update(data, options) + return res } /** * 删除文档 */ async remove(): Promise { - const query = { [this.primaryKey]: this.id } - const param = { - collectionName: this._coll, - query: query, - queryType: QueryType.DOC, - multi: false - } - - const res = await this.request.send('database.deleteDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - deleted: undefined, - ok: false - } - } - - return { - deleted: res.data.deleted, - requestId: res.requestId, - ok: true - } + const res = await this._query + .where({ [this._db.primaryKey]: this.id }) + .remove({ multi: false }) + return res } /** * 返回选中的文档 */ async get(): Promise> { - - const query = { [this.primaryKey]: this.id } - const param = { - collectionName: this._coll, - query: query, - queryType: QueryType.DOC, - multi: false, - projection: this.projection - } - - const res = await this.request.send('database.queryDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - ok: false - } - } - - const docs = res.data.list - const data: any = docs?.length ? docs[0] : undefined - return { - data: data, - requestId: res.requestId, - ok: true - } + const res = await this._query + .where({ [this._db.primaryKey]: this.id }) + .getOne() + return res } /** + * 指定要返回的字段 * + * @param projection */ - field(projection: Object): DocumentReference { - for (let k in projection) { - if (projection[k]) { - projection[k] = 1 - } else { - projection[k] = 0 - } - } - return new DocumentReference(this._db, this._coll, this.id, projection) + field(projection: string[] | ProjectionType): DocumentReference { + return new DocumentReference(this._db, this._coll, this.id, this._query.field(projection)) } } diff --git a/packages/database-ql/src/interface.ts b/packages/database-ql/src/interface.ts index 4a1495e23a..eb459264e6 100644 --- a/packages/database-ql/src/interface.ts +++ b/packages/database-ql/src/interface.ts @@ -1,5 +1,6 @@ +import { ActionType, OrderByDirection } from "./constant" -interface ResponseStruct { +export interface ResponseStruct { code: number data: any error: string @@ -8,5 +9,31 @@ interface ResponseStruct { } export interface RequestInterface { - send(action: string, data: object): Promise + send(action: ActionType, data: QueryParam): Promise } + +export interface QueryOrder { + field: string + direction: OrderByDirection +} + +export interface ProjectionType { + [field: string]: 0 | 1 +} + +export interface QueryParam { + collectionName: string + query?: Object + order?: QueryOrder[] + offset?: number + limit?: number + projection?: ProjectionType + + /** + * Update options + */ + multi?: boolean + merge?: boolean + upsert?: boolean + data?: any +} \ No newline at end of file diff --git a/packages/database-ql/src/query.ts b/packages/database-ql/src/query.ts index 6869e152f6..78a608e1ac 100644 --- a/packages/database-ql/src/query.ts +++ b/packages/database-ql/src/query.ts @@ -1,4 +1,4 @@ -import { OrderByDirection, QueryType } from './constant' +import { ActionType, OrderByDirection } from './constant' import { Db } from './index' import { Validate } from './validate' // import { Util } from './util' @@ -6,39 +6,21 @@ import { QuerySerializer } from './serializer/query' import { UpdateSerializer } from './serializer/update' import { ErrorCode } from './constant' import { GetOneRes, GetRes, CountRes, UpdateRes, RemoveRes } from './result-types' -import { RequestInterface } from './interface' +import { ProjectionType, QueryOrder, RequestInterface, QueryParam } from './interface' import { Util } from './util' +import { serialize } from './serializer/datatype' -interface QueryOrder { - field?: string - direction?: 'asc' | 'desc' -} - interface QueryOption { // 查询数量 limit?: number // 偏移量 offset?: number // 指定显示或者不显示哪些字段 - projection?: Object + projection?: ProjectionType } -// left, right, inner, full -enum JoinType { - INNER = 'inner', - LEFT = 'left', - RIGHT = 'right', - FULL = 'full' -} - -interface JoinParam { - collection: string, - type: JoinType, - leftKey: string, // 左表连接键 - rightKey: string // 右表连接键 -} interface WithParam { /** @@ -69,82 +51,50 @@ interface WithParam { /** - * 查询模块 - * - * @author haroldhu + * Db query */ export class Query { /** - * Db 的引用 - * - * @internal + * Reference to db instance */ protected _db: Db /** * Collection name - * - * @internal */ protected _coll: string /** - * 过滤条件 - * - * @internal + * Query conditions */ private _fieldFilters: Object /** - * 排序条件 - * - * @internal + * Order by conditions */ private _fieldOrders: QueryOrder[] /** - * 查询条件 - * - * @internal + * Query options */ private _queryOptions: QueryOption /** - * 联表条件(join) - * - * @internal - */ - private _joins: JoinParam[] - - /** - * 子表查询(一对多) - * - * @internal + * Sub queries */ private _withs: WithParam[] /** - * 原始过滤参数 - */ - // private _rawWhereParams: Object - - /** - * 请求实例 - * - * @internal + * Request instance */ private _request: RequestInterface /** - * 初始化 - * - * @internal - * - * @param db - 数据库的引用 - * @param coll - 集合名称 - * @param fieldFilters - 过滤条件 - * @param fieldOrders - 排序条件 - * @param queryOptions - 查询条件 + * @param db - db reference + * @param coll - collection name + * @param fieldFilters - query condition + * @param fieldOrders - order by condition + * @param queryOptions - query options */ public constructor( db: Db, @@ -152,18 +102,14 @@ export class Query { fieldFilters?: Object, fieldOrders?: QueryOrder[], queryOptions?: QueryOption, - joins?: JoinParam[], withs?: WithParam[] - // rawWhereParams?: Object ) { this._db = db this._coll = coll this._fieldFilters = fieldFilters this._fieldOrders = fieldOrders || [] this._queryOptions = queryOptions || {} - this._joins = joins || [] this._withs = withs || [] - /* eslint-disable new-cap */ this._request = this._db.request } @@ -195,7 +141,6 @@ export class Query { _query, this._fieldOrders, this._queryOptions, - this._joins, this._withs ) } @@ -222,78 +167,10 @@ export class Query { this._fieldFilters, combinedOrders, this._queryOptions, - this._joins, this._withs ) } - /** - * 添加联表条件,实联接,即数据库支持的联表操作(仅 SQL 数据库支持) - * @param type 联接类型, 以下值之一 "left", "inner", "right", "full" - * @param collection 联接的子表名 - * @param rightKey 子表的联接键名 - * @param leftKey 主表的联接键名 - */ - public join(collection: string, rightKey: string, leftKey: string, type: JoinType = JoinType.INNER): Query { - const newJoin: JoinParam = { - type, - collection, - rightKey, - leftKey - } - - const combinedJoins = this._joins.concat(newJoin) - return new Query( - this._db, - this._coll, - this._fieldFilters, - this._fieldOrders, - this._queryOptions, - combinedJoins, - this._withs - ) - } - - /** - * 添加 left join 联表条件,实联接,即数据库支持的联表操作(仅 SQL 数据库支持) - * @param collection 联接的子表名 - * @param rightKey 子表的联接键名 - * @param leftKey 主表的联接键名 - */ - public leftJoin(collection: string, rightKey: string, leftKey: string): Query { - return this.join(collection, rightKey, leftKey, JoinType.LEFT) - } - - /** - * 添加 right join 联表条件,实联接,即数据库支持的联表操作(仅 SQL 数据库支持) - * @param collection 联接的子表名 - * @param rightKey 子表的联接键名 - * @param leftKey 主表的联接键名 - */ - public rightJoin(collection: string, rightKey: string, leftKey: string): Query { - return this.join(collection, rightKey, leftKey, JoinType.RIGHT) - } - - /** - * 添加 full join 联表条件,实联接,即数据库支持的联表操作(仅 SQL 数据库支持) - * @param collection 联接的子表名 - * @param rightKey 子表的联接键名 - * @param leftKey 主表的联接键名 - */ - public fullJoin(collection: string, rightKey: string, leftKey: string): Query { - return this.join(collection, rightKey, leftKey, JoinType.FULL) - } - - /** - * 添加 inner join 联表条件,实联接,即数据库支持的联表操作(仅 SQL 数据库支持) - * @param collection 联接的子表名 - * @param rightKey 子表的联接键名 - * @param leftKey 主表的联接键名 - */ - public innerJoin(collection: string, rightKey: string, leftKey: string) { - return this.join(collection, rightKey, leftKey, JoinType.INNER) - } - /** * 添加 一对多 子查询条件,需要使用 merge() 代替 get() 发起数据请求 * @param param {WithParam} @@ -309,7 +186,7 @@ export class Query { } const combinedWiths = this._withs.concat(newWith) - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, this._joins, combinedWiths) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, combinedWiths) } /** @@ -327,44 +204,44 @@ export class Query { } const combinedWiths = this._withs.concat(newWith) - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, this._joins, combinedWiths) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, combinedWiths) } /** * 指定要返回的字段 * - * @param projection string[] | {[fieldName]: true | false} + * @param projection */ - public field(projection: string[] | any): Query { + public field(projection: string[] | ProjectionType): Query { + let formatted = {} as ProjectionType if (projection instanceof Array) { - let result = {} + let result = {} as ProjectionType for (let k of projection) { result[k] = 1 } - projection = result + formatted = result } else { for (let k in projection) { if (projection[k]) { if (typeof projection[k] !== 'object') { - projection[k] = 1 + formatted[k] = 1 } } else { - projection[k] = 0 + formatted[k] = 0 } } } + const option = { ...this._queryOptions } + option.projection = formatted - let option = { ...this._queryOptions } - option.projection = projection - - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._joins, this._withs) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._withs) } /** * 设置查询条数 * - * @param limit - 限制条数 + * @param limit - 限制条数,当前限制一次请求获取数据条数不得大于 1000 */ public limit(limit: number): Query { Validate.isInteger('limit', limit) @@ -372,7 +249,7 @@ export class Query { let option = { ...this._queryOptions } option.limit = limit - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._joins, this._withs) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._withs) } /** @@ -386,7 +263,7 @@ export class Query { let option = { ...this._queryOptions } option.offset = offset - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._joins, this._withs) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, option, this._withs) } /** @@ -394,43 +271,177 @@ export class Query { * @returns Query */ public clone(): Query { - return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, this._joins, this._withs) + return new Query(this._db, this._coll, this._fieldFilters, this._fieldOrders, this._queryOptions, this._withs) } /** * 发起请求获取文档列表 * - * - 默认获取集合下全部文档数据 - * - 可以把通过 `orderBy`、`where`、`skip`、`limit`设置的数据添加请求参数上 + * - 默认 `limit` 为 100 + * - 可以把通过 `orderBy()`、`where()`、`skip()`、`limit()`设置的数据添加请求参数上 + */ + public async get(): Promise> { + if (this._withs?.length) { + return await this.internalMerge() + } else { + return await this.internalGet() + } + } + + /** + * 发起请求获取一个文档 + * @param options + * @returns + */ + public async getOne(): Promise> { + const res = await this.limit(1).get() + if (res.error) { + return res as any + } + + if (!res.data.length) { + return { + ok: true, + data: null, + requestId: res.requestId + } + } + + return { + ok: true, + data: res.data[0], + requestId: res.requestId + } + } + + /** + * 发起请求获取文档列表,当使用 with 条件时使用 + * + * @deprecated 该接口已废弃,直接使用 `get()` 代替 + * + * 1. 调用 get() 执行主查询 + * 2. 结合主查询的结果,使用 in 执行子表查询 + * 3. 合并主表 & 子表的结果,即聚合 + * 4. intersection 可指定是否取两个结果集的交集,缺省则以主表结果为主 + */ + public async merge(options?: { intersection?: boolean }): Promise> { + const res = await this.internalMerge(options) + return res + } + + /** + * 获取总数 + */ + public async count(): Promise { + const param = this.buildQueryParam() + const res = await this.send(ActionType.count, param) + + if (res.error) { + return { + requestId: res.requestId, + ok: false, + error: res.error, + total: undefined, + code: res.code + } + } + + return { + requestId: res.requestId, + total: res.data.total, + ok: true + } + } + + /** + * 发起请求批量更新文档 + * + * @param data 数据 + */ + public async update(data: Object, options?: { multi?: boolean, merge?: boolean, upsert?: boolean }): Promise { + + if (!data || typeof data !== 'object' || 0 === Object.keys(data)?.length) { + throw new Error('data cannot be empty object') + } + + if (data.hasOwnProperty('_id')) { + throw new Error('can not update the `_id` field') + } + + const param = this.buildQueryParam() + param.multi = options?.multi ?? false + param.merge = options?.merge ?? true + param.upsert = options?.upsert ?? false + if (param.merge) { + param.data = UpdateSerializer.encode(data) + } else { + param.data = serialize(data) + } + + const res = await this.send(ActionType.update, param) + if (res.error) { + return { + requestId: res.requestId, + error: res.error, + ok: false, + code: res.code + } + } + + return { + requestId: res.requestId, + updated: res.data.updated, + upsertId: res.data.upsert_id, + ok: true + } + } + + /** + * 条件删除文档 */ - public async get(options?: { stat?: boolean, nested?: boolean }): Promise> { - let newOder = [] - if (this._fieldOrders) { - this._fieldOrders.forEach(order => { - newOder.push(order) - }) + public async remove(options?: { multi: boolean }): Promise { + if (Object.keys(this._queryOptions).length > 0) { + console.warn('`offset`, `limit` and `projection` are not supported in remove() operation') } - interface Param { - collectionName: string - query?: Object - queryType: QueryType - order?: string[] - offset?: number - limit?: number - projection?: Object, - joins?: JoinParam[], - nested?: boolean, - stat?: boolean + + if (this._fieldOrders?.length > 0) { + console.warn('`orderBy` is not supported in remove() operation') } - let param: Param = { + + const param = this.buildQueryParam() + param.multi = options.multi ?? false + + const res = await this.send(ActionType.remove, param) + if (res.error) { + return { + requestId: res.requestId, + error: res.error, + ok: false, + deleted: undefined, + code: res.code + } + } + + return { + requestId: res.requestId, + deleted: res.data.deleted, + ok: true + } + } + + /** + * Build query param + * @returns + */ + protected buildQueryParam() { + const param: QueryParam = { collectionName: this._coll, - queryType: QueryType.WHERE, } if (this._fieldFilters) { param.query = this._fieldFilters } - if (newOder.length > 0) { - param.order = newOder + if (this._fieldOrders?.length) { + param.order = [...this._fieldOrders] } if (this._queryOptions.offset) { param.offset = this._queryOptions.offset @@ -443,14 +454,16 @@ export class Query { if (this._queryOptions.projection) { param.projection = this._queryOptions.projection } - if (this._joins.length) { - param.joins = this._joins - } - if (options) { - param.nested = options.nested ?? false - param.stat = options.stat ?? false - } - const res = await this._request.send('database.queryDocument', param) + + return param + } + + /** + * 发起请求获取文档列表 + */ + protected async internalGet(): Promise> { + const param = this.buildQueryParam() + const res = await this.send(ActionType.query, param) if (res.error) { return { error: res.error, @@ -461,8 +474,8 @@ export class Query { } } - const documents = Util.formatResDocumentData(res.data.list) - const result: any = { + const documents: any[] = Util.formatResDocumentData(res.data.list) + const result: GetRes = { data: documents, requestId: res.requestId, ok: true @@ -473,47 +486,21 @@ export class Query { return result } - /** - * 发起请求获取一个文档 - * @param options - * @returns - */ - public async getOne(options?: { nested?: boolean }): Promise> { - const res = await this.get(options) - if (res.error) { - return res as any - } - - if (!res.data.length) { - return { - ok: true, - data: null, - requestId: res.requestId - } - } - - return { - ok: true, - data: res.data[0], - requestId: res.requestId - } as any - } - /** * 发起请求获取文档列表,当使用 with 条件时使用 * - * 1. 调用 get() 执行主查询 + * 1. 调用 internalGet() 执行主查询 * 2. 结合主查询的结果,使用 in 执行子表查询 * 3. 合并主表 & 子表的结果,即聚合 * 4. intersection 可指定是否取两个结果集的交集,缺省则以主表结果为主 */ - public async merge(options?: { nested?: boolean, intersection?: boolean }): Promise> { + protected async internalMerge(options?: { intersection?: boolean }): Promise> { options = options ?? {} as any const intersection = options.intersection ?? false // 调用 get() 执行主查询 - const res = await this.get(options as any) + const res = await this.internalGet() if (!res.ok) { return res as any } @@ -578,161 +565,13 @@ export class Query { return res as any } - - /** - * 获取总数 - */ - public async count(): Promise { - interface Param { - collectionName: string - query?: Object - queryType: QueryType, - joins?: JoinParam[] - } - let param: Param = { - collectionName: this._coll, - queryType: QueryType.WHERE - } - if (this._fieldFilters) { - param.query = this._fieldFilters - } - if (this._joins.length) { - param.joins = this._joins - } - - const res = await this._request.send('database.countDocument', param) - - if (res.error) { - return { - requestId: res.requestId, - ok: false, - error: res.error, - total: undefined, - code: res.code - } - } - - return { - requestId: res.requestId, - total: res.data.total, - ok: true - } - } - - /** - * 发起请求批量更新文档 - * - * @param data 数据 - */ - public async update(data: Object, options?: { multi: boolean, merge: boolean, upsert: boolean }): Promise { - if (!options) { - options = { - multi: false, - merge: true, - upsert: false - } - } else { - options.multi = options.multi ?? false - options.merge = options.merge ?? true - options.upsert = options.upsert ?? false - } - - if (!data || typeof data !== 'object') { - return Promise.resolve({ - code: 'INVALID_PARAM', - error: '参数必需是非空对象' - } as any) - } - - if (data.hasOwnProperty('_id')) { - return Promise.resolve({ - code: 'INVALID_PARAM', - error: '不能更新 _id 的值' - } as any) - } - - let param = { - collectionName: this._coll, - query: this._fieldFilters, - queryType: QueryType.WHERE, - // query: QuerySerializer.encode(this._fieldFilters), - multi: options.multi, - merge: options.merge, - upsert: options.upsert, - data: UpdateSerializer.encode(data), - joins: undefined - // data: Util.encodeDocumentDataForReq(data, true) - // data: this.convertParams(data) - } - if (this._joins.length) { - param.joins = this._joins - } - - const res = await this._request.send('database.updateDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - ok: false, - code: res.code - } - } - - return { - requestId: res.requestId, - updated: res.data.updated, - upsertId: res.data.upsert_id, - ok: true - } - } - - /** - * 条件删除文档 + * Send query request + * @param action + * @param param + * @returns */ - public async remove(options?: { multi: boolean }): Promise { - - if (!options) { - options = { multi: false } - } else { - options.multi = options.multi ?? false - } - - if (Object.keys(this._queryOptions).length > 0) { - console.warn('`offset`, `limit` and `projection` are not supported in remove() operation') - } - - if (this._fieldOrders.length > 0) { - console.warn('`orderBy` is not supported in remove() operation') - } - - const param = { - collectionName: this._coll, - query: QuerySerializer.encode(this._fieldFilters), - queryType: QueryType.WHERE, - multi: options.multi, - joins: undefined - } - - if (this._joins.length) { - param.joins = this._joins - } - - const res = await this._request.send('database.deleteDocument', param) - if (res.error) { - return { - requestId: res.requestId, - error: res.error, - ok: false, - deleted: undefined, - code: res.code - } - } - - return { - requestId: res.requestId, - deleted: res.data.deleted, - ok: true - } + public async send(action: ActionType, param: QueryParam) { + return await this._request.send(action, param) } }