query.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. import { QueryType } from './constant';
  2. import { Db } from './index';
  3. import { Validate } from './validate';
  4. import { Util } from './util';
  5. import { QuerySerializer } from './serializer/query';
  6. import { UpdateSerializer } from './serializer/update';
  7. import { RealtimeWebSocketClient } from './realtime/websocket-client';
  8. import { ErrorCode } from './constant';
  9. import { getReqOpts, stringifyByEJSON, processReturn } from './utils/utils';
  10. import { ERRORS } from './const/code';
  11. import { EJSON } from 'bson';
  12. export class Query {
  13. constructor(db, coll, fieldFilters, apiOptions, transactionId) {
  14. this.watch = (options) => {
  15. if (!Db.ws) {
  16. Db.ws = new RealtimeWebSocketClient({
  17. context: {
  18. appConfig: {
  19. docSizeLimit: 1000,
  20. realtimePingInterval: 10000,
  21. realtimePongWaitTimeout: 5000,
  22. request: this._request
  23. }
  24. }
  25. });
  26. }
  27. const { limit, order } = this._apiOptions;
  28. return Db.ws.watch(Object.assign(Object.assign({}, options), { envId: this._db.config.env, collectionName: this._coll, query: JSON.stringify(this._fieldFilters), limit, orderBy: order
  29. ? order.reduce((acc, cur) => {
  30. acc[cur.field] = cur.direction;
  31. return acc;
  32. }, {})
  33. : undefined }));
  34. };
  35. this._db = db;
  36. this._coll = coll;
  37. this._fieldFilters = fieldFilters;
  38. this._apiOptions = apiOptions || {};
  39. this._request = new Db.reqClass(this._db.config);
  40. this._transactionId = transactionId;
  41. }
  42. async get() {
  43. const order = this._apiOptions.order;
  44. let param = {
  45. collectionName: this._coll,
  46. queryType: QueryType.WHERE,
  47. transactionId: this._transactionId
  48. };
  49. if (this._fieldFilters) {
  50. param.query = this._fieldFilters;
  51. }
  52. if (order) {
  53. param.order = stringifyByEJSON(order);
  54. }
  55. const offset = this._apiOptions.offset;
  56. if (offset) {
  57. param.offset = offset;
  58. }
  59. const limit = this._apiOptions.limit;
  60. if (limit) {
  61. param.limit = limit < 1000 ? limit : 1000;
  62. }
  63. else {
  64. param.limit = 100;
  65. }
  66. const projection = this._apiOptions.projection;
  67. if (projection) {
  68. param.projection = stringifyByEJSON(projection);
  69. }
  70. const res = await this._request.send('database.getDocument', param, getReqOpts(this._apiOptions));
  71. if (res.code) {
  72. return res;
  73. }
  74. const list = res.data.list.map(item => EJSON.parse(item));
  75. const documents = Util.formatResDocumentData(list);
  76. const result = {
  77. data: documents,
  78. requestId: res.requestId
  79. };
  80. if (res.limit)
  81. result.limit = res.limit;
  82. if (res.offset)
  83. result.offset = res.offset;
  84. return result;
  85. }
  86. async count() {
  87. let param = {
  88. collectionName: this._coll,
  89. queryType: QueryType.WHERE
  90. };
  91. if (this._fieldFilters) {
  92. param.query = this._fieldFilters;
  93. }
  94. const res = await this._request.send('database.calculateDocument', param, getReqOpts(this._apiOptions));
  95. if (res.code) {
  96. return res;
  97. }
  98. return {
  99. requestId: res.requestId,
  100. total: res.data.total
  101. };
  102. }
  103. where(query) {
  104. if (Object.prototype.toString.call(query).slice(8, -1) !== 'Object') {
  105. throw Error(ErrorCode.QueryParamTypeError);
  106. }
  107. const keys = Object.keys(query);
  108. const checkFlag = keys.some(item => {
  109. return query[item] !== undefined;
  110. });
  111. if (keys.length && !checkFlag) {
  112. throw Error(ErrorCode.QueryParamValueError);
  113. }
  114. return new Query(this._db, this._coll, QuerySerializer.encodeEJSON(query), this._apiOptions, this._transactionId);
  115. }
  116. options(apiOptions) {
  117. Validate.isValidOptions(apiOptions);
  118. return new Query(this._db, this._coll, this._fieldFilters, apiOptions, this._transactionId);
  119. }
  120. orderBy(fieldPath, directionStr) {
  121. Validate.isFieldPath(fieldPath);
  122. Validate.isFieldOrder(directionStr);
  123. const newOrder = {
  124. [fieldPath]: directionStr === 'desc' ? -1 : 1
  125. };
  126. const order = this._apiOptions.order || {};
  127. const newApiOption = Object.assign({}, this._apiOptions, {
  128. order: Object.assign({}, order, newOrder)
  129. });
  130. return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
  131. }
  132. limit(limit) {
  133. Validate.isInteger('limit', limit);
  134. let newApiOption = Object.assign({}, this._apiOptions);
  135. newApiOption.limit = limit;
  136. return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
  137. }
  138. skip(offset) {
  139. Validate.isInteger('offset', offset);
  140. let newApiOption = Object.assign({}, this._apiOptions);
  141. newApiOption.offset = offset;
  142. return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
  143. }
  144. async update(data) {
  145. if (!data || typeof data !== 'object') {
  146. return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
  147. }
  148. if (data.hasOwnProperty('_id')) {
  149. return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
  150. }
  151. let { multiple } = this._apiOptions;
  152. const multi = multiple === undefined ? true : multiple;
  153. let param = {
  154. collectionName: this._coll,
  155. queryType: QueryType.WHERE,
  156. multi,
  157. merge: true,
  158. upsert: false,
  159. data: UpdateSerializer.encodeEJSON(data)
  160. };
  161. if (this._fieldFilters) {
  162. param.query = this._fieldFilters;
  163. }
  164. const res = await this._request.send('database.modifyDocument', param, getReqOpts(this._apiOptions));
  165. if (res.code) {
  166. return res;
  167. }
  168. return {
  169. requestId: res.requestId,
  170. updated: res.data.updated,
  171. upsertId: res.data.upsert_id
  172. };
  173. }
  174. field(projection) {
  175. let transformProjection = {};
  176. for (let k in projection) {
  177. if (typeof projection[k] === 'boolean') {
  178. transformProjection[k] = projection[k] === true ? 1 : 0;
  179. }
  180. if (typeof projection[k] === 'number') {
  181. transformProjection[k] = projection[k] > 0 ? 1 : 0;
  182. }
  183. if (typeof projection[k] === 'object') {
  184. transformProjection[k] = projection[k];
  185. }
  186. }
  187. let newApiOption = Object.assign({}, this._apiOptions);
  188. newApiOption.projection = transformProjection;
  189. return new Query(this._db, this._coll, this._fieldFilters, newApiOption, this._transactionId);
  190. }
  191. async remove() {
  192. const { offset, limit, projection, order } = this._apiOptions;
  193. if (offset !== undefined ||
  194. limit !== undefined ||
  195. projection !== undefined ||
  196. order !== undefined) {
  197. console.warn('`offset`, `limit`, `projection`, `orderBy` are not supported in remove() operation');
  198. }
  199. let { multiple } = this._apiOptions;
  200. const multi = multiple === undefined ? true : multiple;
  201. const param = {
  202. collectionName: this._coll,
  203. query: this._fieldFilters,
  204. queryType: QueryType.WHERE,
  205. multi
  206. };
  207. const res = await this._request.send('database.removeDocument', param, getReqOpts(this._apiOptions));
  208. if (res.code) {
  209. return res;
  210. }
  211. return {
  212. requestId: res.requestId,
  213. deleted: res.data.deleted
  214. };
  215. }
  216. async updateAndReturn(data) {
  217. if (!data || typeof data !== 'object') {
  218. return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '参数必需是非空对象' }));
  219. }
  220. if (data.hasOwnProperty('_id')) {
  221. return processReturn(this._db.config.throwOnCode, Object.assign(Object.assign({}, ERRORS.INVALID_PARAM), { message: '不能更新_id的值' }));
  222. }
  223. let param = {
  224. collectionName: this._coll,
  225. queryType: QueryType.WHERE,
  226. data: UpdateSerializer.encodeEJSON(data)
  227. };
  228. if (this._transactionId) {
  229. param.transactionId = this._transactionId;
  230. }
  231. if (this._fieldFilters) {
  232. param.query = this._fieldFilters;
  233. }
  234. const res = await this._request.send('database.modifyAndReturnDoc', param, getReqOpts(this._apiOptions));
  235. if (res.code) {
  236. return res;
  237. }
  238. return {
  239. requestId: res.requestId,
  240. updated: res.data.updated,
  241. doc: res.data.doc && EJSON.parse(res.data.doc)
  242. };
  243. }
  244. }