import { Db } from './index'; import { Util } from './util'; import { UpdateSerializer } from './serializer/update'; import { serialize } from './serializer/datatype'; import { UpdateCommand } from './commands/update'; import { RealtimeWebSocketClient } from './realtime/websocket-client'; import { QueryType } from './constant'; import { getReqOpts, stringifyByEJSON, processReturn } from './utils/utils'; import { ERRORS } from './const/code'; import { EJSON } from 'bson'; export class DocumentReference { constructor(db, coll, apiOptions, docID, transactionId) { this.watch = (options) => { if (!Db.ws) { Db.ws = new RealtimeWebSocketClient({ context: { appConfig: { docSizeLimit: 1000, realtimePingInterval: 10000, realtimePongWaitTimeout: 5000, request: this.request } } }); } return Db.ws.watch(Object.assign(Object.assign({}, options), { envId: this._db.config.env, collectionName: this._coll, query: JSON.stringify({ _id: this.id }) })); }; this._db = db; this._coll = coll; this.id = docID; this._transactionId = transactionId; this.request = new Db.reqClass(this._db.config); this._apiOptions = apiOptions; } async create(data) { if (this.id) { data['_id'] = this.id; } let params = { collectionName: this._coll, data: [stringifyByEJSON(serialize(data))], transactionId: this._transactionId }; const res = await this.request.send('database.insertDocument', params, getReqOpts(this._apiOptions)); if (res.code) { return res; } if (this._transactionId) { return { inserted: 1, ok: 1, id: res.data.insertedIds[0], requestId: res.requestId }; } return { id: res.data.insertedIds[0], requestId: res.requestId }; } async set(data) { if (!this.id) { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: 'docId不能为空' })); } if (!data || typeof data !== 'object') { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' })); } if (data.hasOwnProperty('_id')) { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '不能更新_id的值' })); } let hasOperator = false; const checkMixed = objs => { if (typeof objs === 'object') { for (let key in objs) { if (objs[key] instanceof UpdateCommand) { hasOperator = true; } else if (typeof objs[key] === 'object') { checkMixed(objs[key]); } } } }; checkMixed(data); if (hasOperator) { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.DATABASE_REQUEST_FAILED), { message: 'update operator complicit' })); } let param = { collectionName: this._coll, queryType: QueryType.DOC, data: stringifyByEJSON(serialize(data)), transactionId: this._transactionId, multi: false, merge: false, upsert: true }; if (this.id) { param['query'] = stringifyByEJSON({ _id: this.id }); } const res = await this.request.send('database.modifyDocument', param, getReqOpts(this._apiOptions)); if (res.code) { return res; } if (this._transactionId) { return { updated: res.data.updated, upserted: [{ _id: res.data.upsert_id }], requestId: res.requestId }; } return { updated: res.data.updated, upsertedId: res.data.upsert_id, requestId: res.requestId }; } async update(data) { if (!data || typeof data !== 'object') { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' })); } if (data.hasOwnProperty('_id')) { return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '不能更新_id的值' })); } const query = stringifyByEJSON({ _id: this.id }); const param = { collectionName: this._coll, transactionId: this._transactionId, data: UpdateSerializer.encodeEJSON(data), query, queryType: QueryType.DOC, multi: false, merge: true, upsert: false }; const res = await this.request.send('database.modifyDocument', param, getReqOpts(this._apiOptions)); if (res.code) { return res; } return { updated: res.data.updated, requestId: res.requestId }; } async delete() { return this.remove(); } async remove() { const query = stringifyByEJSON({ _id: this.id }); const param = { collectionName: this._coll, transactionId: this._transactionId, query: query, queryType: QueryType.DOC, multi: false }; const res = await this.request.send('database.removeDocument', param, getReqOpts(this._apiOptions)); if (res.code) { return res; } return { deleted: res.data.deleted, requestId: res.requestId }; } async get() { const query = stringifyByEJSON({ _id: this.id }); const { projection } = this._apiOptions; const param = { collectionName: this._coll, query, transactionId: this._transactionId, queryType: QueryType.DOC, multi: false }; if (projection) { param.projection = stringifyByEJSON(projection); } const res = await this.request.send('database.getDocument', param, getReqOpts(this._apiOptions)); if (res.code) { return res; } const list = res.data.list.map(item => EJSON.parse(item)); const documents = Util.formatResDocumentData(list); if (this._transactionId) { return { data: documents[0] || null, requestId: res.requestId }; } return { data: documents, requestId: res.requestId, offset: res.data.offset, limit: res.data.limit }; } field(projection) { let transformProjection = {}; for (let k in projection) { if (typeof projection[k] === 'boolean') { transformProjection[k] = projection[k] === true ? 1 : 0; } if (typeof projection[k] === 'number') { transformProjection[k] = projection[k] > 0 ? 1 : 0; } if (typeof projection[k] === 'object') { transformProjection[k] = projection[k]; } } let newApiOption = Object.assign({}, this._apiOptions); newApiOption.projection = transformProjection; return new DocumentReference(this._db, this._coll, newApiOption, this.id, this._transactionId); } }