@cloudbase/database 1.4.3 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (197) hide show
  1. package/dist/commonjs/ObjectId/index.d.ts +1 -1
  2. package/dist/commonjs/ObjectId/index.js +16 -9
  3. package/dist/commonjs/aggregate.js +126 -75
  4. package/dist/commonjs/collection.d.ts +3 -2
  5. package/dist/commonjs/collection.js +131 -58
  6. package/dist/commonjs/command.d.ts +5 -5
  7. package/dist/commonjs/command.js +181 -154
  8. package/dist/commonjs/commands/logic.js +30 -20
  9. package/dist/commonjs/commands/query.d.ts +1 -1
  10. package/dist/commonjs/commands/query.js +85 -65
  11. package/dist/commonjs/commands/update.js +9 -7
  12. package/dist/commonjs/config/error.config.js +1 -0
  13. package/dist/commonjs/const/code.js +1 -0
  14. package/dist/commonjs/constant.d.ts +7 -7
  15. package/dist/commonjs/constant.js +13 -11
  16. package/dist/commonjs/document.d.ts +10 -6
  17. package/dist/commonjs/document.js +305 -193
  18. package/dist/commonjs/geo/index.js +20 -9
  19. package/dist/commonjs/geo/lineString.d.ts +1 -1
  20. package/dist/commonjs/geo/lineString.js +62 -33
  21. package/dist/commonjs/geo/multiLineString.d.ts +1 -1
  22. package/dist/commonjs/geo/multiLineString.js +71 -32
  23. package/dist/commonjs/geo/multiPoint.d.ts +1 -1
  24. package/dist/commonjs/geo/multiPoint.js +58 -29
  25. package/dist/commonjs/geo/multiPolygon.d.ts +1 -1
  26. package/dist/commonjs/geo/multiPolygon.js +96 -36
  27. package/dist/commonjs/geo/point.d.ts +1 -1
  28. package/dist/commonjs/geo/point.js +30 -23
  29. package/dist/commonjs/geo/polygon.d.ts +1 -1
  30. package/dist/commonjs/geo/polygon.js +78 -39
  31. package/dist/commonjs/helper/symbol.js +17 -5
  32. package/dist/commonjs/index.d.ts +8 -2
  33. package/dist/commonjs/index.js +73 -20
  34. package/dist/commonjs/lib/util.d.ts +1 -0
  35. package/dist/commonjs/lib/util.js +26 -0
  36. package/dist/commonjs/operator-map.js +7 -6
  37. package/dist/commonjs/query.d.ts +9 -5
  38. package/dist/commonjs/query.js +330 -205
  39. package/dist/commonjs/regexp/index.d.ts +1 -1
  40. package/dist/commonjs/regexp/index.js +16 -9
  41. package/dist/commonjs/serializer/common.d.ts +2 -2
  42. package/dist/commonjs/serializer/common.js +59 -22
  43. package/dist/commonjs/serializer/datatype.d.ts +2 -2
  44. package/dist/commonjs/serializer/datatype.js +58 -21
  45. package/dist/commonjs/serializer/query.d.ts +1 -1
  46. package/dist/commonjs/serializer/query.js +137 -127
  47. package/dist/commonjs/serializer/update.d.ts +1 -1
  48. package/dist/commonjs/serializer/update.js +83 -78
  49. package/dist/commonjs/serverDate/index.d.ts +1 -1
  50. package/dist/commonjs/serverDate/index.js +16 -9
  51. package/dist/commonjs/transaction/collection.d.ts +7 -0
  52. package/dist/commonjs/transaction/collection.js +49 -0
  53. package/dist/commonjs/transaction/document.d.ts +27 -0
  54. package/dist/commonjs/transaction/document.js +205 -0
  55. package/dist/commonjs/transaction/index.js +196 -86
  56. package/dist/commonjs/transaction/query.d.ts +43 -0
  57. package/dist/commonjs/transaction/query.js +261 -0
  58. package/dist/commonjs/typings/index.d.ts +4 -4
  59. package/dist/commonjs/typings/realtime.d.ts +16 -16
  60. package/dist/commonjs/util.js +123 -92
  61. package/dist/commonjs/utils/error.d.ts +2 -1
  62. package/dist/commonjs/utils/error.js +74 -38
  63. package/dist/commonjs/utils/symbol.js +33 -14
  64. package/dist/commonjs/utils/type.d.ts +1 -1
  65. package/dist/commonjs/utils/type.js +25 -13
  66. package/dist/commonjs/utils/utils.d.ts +5 -1
  67. package/dist/commonjs/utils/utils.js +153 -22
  68. package/dist/commonjs/validate.js +79 -63
  69. package/dist/esm/ObjectId/index.d.ts +1 -1
  70. package/dist/esm/ObjectId/index.js +15 -8
  71. package/dist/esm/aggregate.js +118 -66
  72. package/dist/esm/collection.d.ts +3 -2
  73. package/dist/esm/collection.js +125 -52
  74. package/dist/esm/command.d.ts +5 -5
  75. package/dist/esm/command.js +175 -147
  76. package/dist/esm/commands/logic.js +30 -20
  77. package/dist/esm/commands/query.d.ts +1 -1
  78. package/dist/esm/commands/query.js +91 -71
  79. package/dist/esm/commands/update.js +8 -6
  80. package/dist/esm/config/error.config.js +1 -1
  81. package/dist/esm/const/code.js +1 -1
  82. package/dist/esm/constant.d.ts +7 -7
  83. package/dist/esm/constant.js +12 -11
  84. package/dist/esm/document.d.ts +10 -6
  85. package/dist/esm/document.js +297 -185
  86. package/dist/esm/geo/interface.js +1 -0
  87. package/dist/esm/geo/lineString.d.ts +1 -1
  88. package/dist/esm/geo/lineString.js +57 -28
  89. package/dist/esm/geo/multiLineString.d.ts +1 -1
  90. package/dist/esm/geo/multiLineString.js +66 -27
  91. package/dist/esm/geo/multiPoint.d.ts +1 -1
  92. package/dist/esm/geo/multiPoint.js +53 -24
  93. package/dist/esm/geo/multiPolygon.d.ts +1 -1
  94. package/dist/esm/geo/multiPolygon.js +91 -31
  95. package/dist/esm/geo/point.d.ts +1 -1
  96. package/dist/esm/geo/point.js +26 -19
  97. package/dist/esm/geo/polygon.d.ts +1 -1
  98. package/dist/esm/geo/polygon.js +73 -34
  99. package/dist/esm/helper/symbol.js +13 -13
  100. package/dist/esm/index.d.ts +8 -2
  101. package/dist/esm/index.js +62 -9
  102. package/dist/esm/lib/util.d.ts +1 -0
  103. package/dist/esm/lib/util.js +22 -0
  104. package/dist/esm/operator-map.js +4 -4
  105. package/dist/esm/query.d.ts +9 -5
  106. package/dist/esm/query.js +321 -196
  107. package/dist/esm/regexp/index.d.ts +1 -1
  108. package/dist/esm/regexp/index.js +15 -8
  109. package/dist/esm/serializer/common.d.ts +2 -2
  110. package/dist/esm/serializer/common.js +49 -13
  111. package/dist/esm/serializer/datatype.d.ts +2 -2
  112. package/dist/esm/serializer/datatype.js +44 -8
  113. package/dist/esm/serializer/query.d.ts +1 -1
  114. package/dist/esm/serializer/query.js +112 -102
  115. package/dist/esm/serializer/update.d.ts +1 -1
  116. package/dist/esm/serializer/update.js +69 -64
  117. package/dist/esm/serverDate/index.d.ts +1 -1
  118. package/dist/esm/serverDate/index.js +15 -8
  119. package/dist/esm/transaction/collection.d.ts +7 -0
  120. package/dist/esm/transaction/collection.js +46 -0
  121. package/dist/esm/transaction/document.d.ts +27 -0
  122. package/dist/esm/transaction/document.js +202 -0
  123. package/dist/esm/transaction/index.js +191 -81
  124. package/dist/esm/transaction/query.d.ts +43 -0
  125. package/dist/esm/transaction/query.js +258 -0
  126. package/dist/esm/typings/index.d.ts +4 -4
  127. package/dist/esm/typings/index.js +1 -0
  128. package/dist/esm/typings/realtime.d.ts +16 -16
  129. package/dist/esm/typings/realtime.js +1 -0
  130. package/dist/esm/util.js +121 -90
  131. package/dist/esm/utils/error.d.ts +2 -1
  132. package/dist/esm/utils/error.js +70 -35
  133. package/dist/esm/utils/symbol.js +33 -14
  134. package/dist/esm/utils/type.d.ts +1 -1
  135. package/dist/esm/utils/type.js +12 -12
  136. package/dist/esm/utils/utils.d.ts +5 -1
  137. package/dist/esm/utils/utils.js +137 -16
  138. package/dist/esm/validate.js +65 -49
  139. package/jest.config.js +14 -0
  140. package/package.json +14 -10
  141. package/src/aggregate.ts +1 -5
  142. package/src/collection.ts +15 -2
  143. package/src/document.ts +45 -34
  144. package/src/index.ts +51 -4
  145. package/src/query.ts +48 -38
  146. package/src/serializer/query.ts +18 -6
  147. package/src/transaction/index.ts +1 -1
  148. package/src/typings/index.ts +0 -4
  149. package/src/utils/utils.ts +105 -0
  150. package/src/validate.ts +3 -6
  151. package/tests/gateway-contract.test.ts +180 -0
  152. package/tests/index.test.ts +283 -0
  153. package/tests/transaction-query.test.ts +207 -0
  154. package/tests/watch-query.test.ts +144 -0
  155. package/tsconfig.esm.json +4 -2
  156. package/tsconfig.json +16 -6
  157. package/tsconfig.test.json +15 -0
  158. package/dist/commonjs/realtime/error.d.ts +0 -7
  159. package/dist/commonjs/realtime/error.js +0 -11
  160. package/dist/commonjs/realtime/listener.d.ts +0 -14
  161. package/dist/commonjs/realtime/listener.js +0 -17
  162. package/dist/commonjs/realtime/message.d.ts +0 -3
  163. package/dist/commonjs/realtime/message.js +0 -10
  164. package/dist/commonjs/realtime/snapshot.d.ts +0 -16
  165. package/dist/commonjs/realtime/snapshot.js +0 -42
  166. package/dist/commonjs/realtime/virtual-websocket-client.d.ts +0 -63
  167. package/dist/commonjs/realtime/virtual-websocket-client.js +0 -736
  168. package/dist/commonjs/realtime/websocket-client.d.ts +0 -78
  169. package/dist/commonjs/realtime/websocket-client.js +0 -567
  170. package/dist/commonjs/realtime/ws-event.d.ts +0 -126
  171. package/dist/commonjs/realtime/ws-event.js +0 -139
  172. package/dist/commonjs/transaction.d.ts +0 -42
  173. package/dist/commonjs/transaction.js +0 -126
  174. package/dist/esm/realtime/error.d.ts +0 -7
  175. package/dist/esm/realtime/error.js +0 -8
  176. package/dist/esm/realtime/listener.d.ts +0 -14
  177. package/dist/esm/realtime/listener.js +0 -14
  178. package/dist/esm/realtime/message.d.ts +0 -3
  179. package/dist/esm/realtime/message.js +0 -6
  180. package/dist/esm/realtime/snapshot.d.ts +0 -16
  181. package/dist/esm/realtime/snapshot.js +0 -39
  182. package/dist/esm/realtime/virtual-websocket-client.d.ts +0 -63
  183. package/dist/esm/realtime/virtual-websocket-client.js +0 -733
  184. package/dist/esm/realtime/websocket-client.d.ts +0 -78
  185. package/dist/esm/realtime/websocket-client.js +0 -564
  186. package/dist/esm/realtime/ws-event.d.ts +0 -126
  187. package/dist/esm/realtime/ws-event.js +0 -137
  188. package/dist/esm/transaction.d.ts +0 -42
  189. package/dist/esm/transaction.js +0 -121
  190. package/src/realtime/error.ts +0 -19
  191. package/src/realtime/listener.ts +0 -35
  192. package/src/realtime/message.ts +0 -14
  193. package/src/realtime/snapshot.ts +0 -60
  194. package/src/realtime/virtual-websocket-client.ts +0 -1195
  195. package/src/realtime/websocket-client.ts +0 -995
  196. package/src/realtime/ws-event.ts +0 -163
  197. package/src/transaction.ts +0 -190
package/src/document.ts CHANGED
@@ -4,9 +4,8 @@ import { UpdateSerializer } from './serializer/update'
4
4
  import { serialize } from './serializer/datatype'
5
5
  import { UpdateCommand } from './commands/update'
6
6
  import { IWatchOptions, DBRealtimeListener } from './typings/index'
7
- import { RealtimeWebSocketClient } from './realtime/websocket-client'
8
7
  import { QueryType } from './constant'
9
- import { getReqOpts, stringifyByEJSON, processReturn } from './utils/utils'
8
+ import { getWsInstance, getReqOpts, stringifyByEJSON, processReturn, withCallback } from './utils/utils'
10
9
  import { ERRORS } from './const/code'
11
10
  import { EJSON } from 'bson'
12
11
  import { QueryOption, UpdateOption } from './query'
@@ -24,11 +23,6 @@ export class DocumentReference {
24
23
 
25
24
  readonly _transactionId: string
26
25
 
27
- /**
28
- *
29
- */
30
- readonly projection: Object
31
-
32
26
  /**
33
27
  * 数据库引用
34
28
  *
@@ -73,7 +67,7 @@ export class DocumentReference {
73
67
  this.id = docID
74
68
  this._transactionId = transactionId
75
69
  /* eslint-disable new-cap*/
76
- this.request = new Db.reqClass(this._db.config)
70
+ this.request = Db.createRequest(this._db.config)
77
71
  this._apiOptions = apiOptions
78
72
  }
79
73
 
@@ -83,16 +77,23 @@ export class DocumentReference {
83
77
  * @param data - 文档数据
84
78
  * @internal
85
79
  */
86
- async create(data: any): Promise<any> {
80
+ create(data: any, callback?: any): Promise<any> {
81
+ return withCallback(this._create(data), callback)
82
+ }
83
+
84
+ private async _create(data: any): Promise<any> {
85
+ const isBatch = Array.isArray(data)
86
+
87
87
  // 存在docid 则放入data
88
- if (this.id) {
88
+ if (!isBatch && this.id) {
89
89
  data['_id'] = this.id
90
90
  }
91
91
 
92
92
  let params = {
93
93
  collectionName: this._coll,
94
- // data: Util.encodeDocumentDataForReq(data, false, false)
95
- data: [stringifyByEJSON(serialize(data))],
94
+ data: isBatch
95
+ ? data.map((item: any) => stringifyByEJSON(serialize(item)))
96
+ : [stringifyByEJSON(serialize(data))],
96
97
  transactionId: this._transactionId
97
98
  }
98
99
 
@@ -116,11 +117,18 @@ export class DocumentReference {
116
117
  }
117
118
  }
118
119
 
120
+ // 批量创建,返回 ids
121
+ if (isBatch) {
122
+ return {
123
+ ids: (res.data && res.data.insertedIds) || [],
124
+ requestId: res.requestId
125
+ }
126
+ }
127
+
119
128
  return {
120
129
  id: res.data.insertedIds[0],
121
130
  requestId: res.requestId
122
131
  }
123
- // }
124
132
  }
125
133
 
126
134
  /**
@@ -132,7 +140,11 @@ export class DocumentReference {
132
140
  * @param data - 文档数据
133
141
  * @param opts - 可选项
134
142
  */
135
- async set(data: Object): Promise<any> {
143
+ set(data: Object, callback?: any): Promise<any> {
144
+ return withCallback(this._set(data), callback)
145
+ }
146
+
147
+ private async _set(data: Object): Promise<any> {
136
148
  if (!this.id) {
137
149
  return processReturn(this._db.config.throwOnCode, {
138
150
  ...ERRORS.INVALID_PARAM,
@@ -226,7 +238,11 @@ export class DocumentReference {
226
238
  * @param data - 文档数据
227
239
  * @param opts - 可选项
228
240
  */
229
- async update(data: Object): Promise<any> {
241
+ update(data: Object, callback?: any): Promise<any> {
242
+ return withCallback(this._update(data), callback)
243
+ }
244
+
245
+ private async _update(data: Object): Promise<any> {
230
246
  if (!data || typeof data !== 'object') {
231
247
  return processReturn(this._db.config.throwOnCode, {
232
248
  ...ERRORS.INVALID_PARAM,
@@ -277,14 +293,18 @@ export class DocumentReference {
277
293
  /**
278
294
  * 删除文档(事务兼容delete接口)
279
295
  */
280
- async delete(): Promise<any> {
281
- return this.remove()
296
+ delete(callback?: any): Promise<any> {
297
+ return this.remove(callback)
282
298
  }
283
299
 
284
300
  /**
285
301
  * 删除文档
286
302
  */
287
- async remove(): Promise<any> {
303
+ remove(callback?: any): Promise<any> {
304
+ return withCallback(this._remove(), callback)
305
+ }
306
+
307
+ private async _remove(): Promise<any> {
288
308
  const query = stringifyByEJSON({ _id: this.id })
289
309
  const param = {
290
310
  collectionName: this._coll,
@@ -317,7 +337,11 @@ export class DocumentReference {
317
337
  /**
318
338
  * 返回选中的文档(_id)
319
339
  */
320
- async get(): Promise<any> {
340
+ get(callback?: any): Promise<any> {
341
+ return withCallback(this._get(), callback)
342
+ }
343
+
344
+ private async _get(): Promise<any> {
321
345
  const query = stringifyByEJSON({ _id: this.id })
322
346
  const { projection } = this._apiOptions as QueryOption
323
347
  const param: any = {
@@ -327,7 +351,6 @@ export class DocumentReference {
327
351
  queryType: QueryType.DOC,
328
352
  multi: false
329
353
  }
330
-
331
354
  if (projection) {
332
355
  param.projection = stringifyByEJSON(projection)
333
356
  }
@@ -387,20 +410,9 @@ export class DocumentReference {
387
410
  * 监听单个文档
388
411
  */
389
412
  watch = (options: IWatchOptions): DBRealtimeListener => {
390
- if (!Db.ws) {
391
- Db.ws = new RealtimeWebSocketClient({
392
- context: {
393
- appConfig: {
394
- docSizeLimit: 1000,
395
- realtimePingInterval: 10000,
396
- realtimePongWaitTimeout: 5000,
397
- request: this.request
398
- }
399
- }
400
- })
401
- }
413
+ const ws = getWsInstance(this._db)
402
414
 
403
- return (Db.ws as RealtimeWebSocketClient).watch({
415
+ return ws.watch({
404
416
  ...options,
405
417
  envId: this._db.config.env,
406
418
  collectionName: this._coll,
@@ -409,6 +421,5 @@ export class DocumentReference {
409
421
  _id: this.id
410
422
  })
411
423
  })
412
- // })
413
424
  }
414
425
  }
package/src/index.ts CHANGED
@@ -5,8 +5,16 @@ import { Command } from './command'
5
5
  import { ServerDateConstructor } from './serverDate/index'
6
6
  import { RegExpConstructor } from './regexp/index'
7
7
  import { startTransaction, runTransaction } from './transaction/index'
8
+ import { LogicCommand } from './commands/logic'
9
+ import { QueryCommand } from './commands/query'
10
+ import { UpdateCommand } from './commands/update'
8
11
  import { ObjectIdConstructor } from './ObjectId/index'
9
12
 
13
+ /**
14
+ * 数据库配置接口(兼容旧名,实际为完整的 CloudBase 配置)
15
+ */
16
+ export type IDbConfig = ICloudBaseConfig
17
+
10
18
  /**
11
19
  * 地理位置类型
12
20
  */
@@ -18,7 +26,7 @@ export { Query } from './query'
18
26
  export { CollectionReference } from './collection'
19
27
  export { DocumentReference } from './document'
20
28
 
21
- interface ICloudBaseConfig {
29
+ export interface ICloudBaseConfig {
22
30
  timeout?: number
23
31
  isHttp?: boolean
24
32
  secretId?: string
@@ -68,12 +76,18 @@ export class Db {
68
76
 
69
77
  runTransaction: any
70
78
 
79
+ logicCommand: any
80
+
81
+ updateCommand: any
82
+
83
+ queryCommand: any
84
+
71
85
  /**
72
86
  * 初始化
73
87
  *
74
88
  * 默认是 `default` 数据库,为今后扩展使用
75
89
  *
76
- * @param config
90
+ * @param config - 数据库配置
77
91
  */
78
92
  config: ICloudBaseConfig
79
93
 
@@ -83,6 +97,8 @@ export class Db {
83
97
 
84
98
  static wsClass: any
85
99
 
100
+ static wsClientClass: any
101
+
86
102
  // 创建签名的工具函数,由jssdk传入
87
103
  static createSign: Function
88
104
 
@@ -94,7 +110,33 @@ export class Db {
94
110
  // 应用签名信息,由jssdk传入
95
111
  static appSecretInfo: any
96
112
 
97
- constructor(config?: any) {
113
+ // 创建请求实例的包装方法,自动添加instance和database参数
114
+ static createRequest(config: ICloudBaseConfig): any {
115
+ const request = new Db.reqClass(config)
116
+ const originalSend = request.send
117
+
118
+ // 包装send方法,自动添加instance和database参数;
119
+ // 剩余参数(如 2.0 引擎的 getReqOpts,含 per-request timeout)原样透传
120
+ request.send = function(action: string, data: any, ...args: any[]): any {
121
+ // data 可能为空(如事务 startTransaction 不带参数),注入前需防护
122
+ if (data) {
123
+ // 如果配置中包含instance或database参数,添加到请求数据中
124
+ if (config.instance) {
125
+ data.instance = config.instance
126
+ }
127
+ if (config.database) {
128
+ data.database = config.database
129
+ }
130
+ }
131
+
132
+ // 调用原始的send方法
133
+ return originalSend.call(this, action, data, ...args)
134
+ }
135
+
136
+ return request
137
+ }
138
+
139
+ constructor(config?: ICloudBaseConfig) {
98
140
  this.config = config
99
141
  this.Geo = Geo
100
142
  this.serverDate = ServerDateConstructor
@@ -103,6 +145,11 @@ export class Db {
103
145
  this.ObjectId = ObjectIdConstructor
104
146
  this.startTransaction = startTransaction
105
147
  this.runTransaction = runTransaction
148
+
149
+ // 暴露command类 给新sdk灰度兼容用
150
+ this.logicCommand = LogicCommand
151
+ this.updateCommand = UpdateCommand
152
+ this.queryCommand = QueryCommand
106
153
  }
107
154
 
108
155
  /**
@@ -121,7 +168,7 @@ export class Db {
121
168
  * 创建集合
122
169
  */
123
170
  createCollection(collName: string) {
124
- let request = new Db.reqClass(this.config)
171
+ let request = Db.createRequest(this.config)
125
172
 
126
173
  const params = {
127
174
  collectionName: collName
package/src/query.ts CHANGED
@@ -9,9 +9,8 @@ import { QuerySerializer } from './serializer/query'
9
9
  import { UpdateSerializer } from './serializer/update'
10
10
  // import { WSClient } from "./websocket/wsclient"
11
11
  import { IWatchOptions, DBRealtimeListener } from './typings/index'
12
- import { RealtimeWebSocketClient } from './realtime/websocket-client'
13
12
  import { ErrorCode } from './constant'
14
- import { getReqOpts, stringifyByEJSON, processReturn } from './utils/utils'
13
+ import { getWsInstance, getReqOpts, stringifyByEJSON, toWatchQuery, processReturn, withCallback } from './utils/utils'
15
14
  import { ERRORS } from './const/code'
16
15
  import { EJSON } from 'bson'
17
16
 
@@ -35,8 +34,8 @@ export interface QueryOption extends BaseOption {
35
34
  offset?: number
36
35
  // 指定显示或者不显示哪些字段
37
36
  projection?: Object
38
- // 结果排序
39
- order?: Record<string, any>[]
37
+ // 结果排序(orderBy() 存为 { field: 1 | -1 })
38
+ order?: Record<string, any>
40
39
  }
41
40
 
42
41
  export interface UpdateOption extends BaseOption {
@@ -156,7 +155,7 @@ export class Query {
156
155
  this._fieldFilters = fieldFilters
157
156
  this._apiOptions = apiOptions || {}
158
157
  /* eslint-disable new-cap */
159
- this._request = new Db.reqClass(this._db.config)
158
+ this._request = Db.createRequest(this._db.config)
160
159
  this._transactionId = transactionId
161
160
  }
162
161
 
@@ -166,7 +165,11 @@ export class Query {
166
165
  * - 默认获取集合下全部文档数据
167
166
  * - 可以把通过 `orderBy`、`where`、`skip`、`limit`设置的数据添加请求参数上
168
167
  */
169
- public async get(): Promise<GetRes> {
168
+ public get(callback?: any): Promise<GetRes> {
169
+ return withCallback(this._get(), callback) as Promise<GetRes>
170
+ }
171
+
172
+ private async _get(): Promise<GetRes> {
170
173
  /* eslint-disable no-param-reassign */
171
174
 
172
175
  const order = (this._apiOptions as QueryOption).order
@@ -176,7 +179,7 @@ export class Query {
176
179
  transactionId?: string
177
180
  query?: Object
178
181
  queryType: QueryType
179
- order?: string[]
182
+ order?: string
180
183
  offset?: number
181
184
  limit?: number
182
185
  projection?: Object
@@ -200,14 +203,8 @@ export class Query {
200
203
  if (offset) {
201
204
  param.offset = offset
202
205
  }
203
-
204
206
  const limit = (this._apiOptions as QueryOption).limit
205
207
 
206
- // if (this._queryOptions.limit) {
207
- // param.limit = this._queryOptions.limit < 1000 ? this._queryOptions.limit : 1000
208
- // } else {
209
- // param.limit = 100
210
- // }
211
208
  if (limit) {
212
209
  param.limit = limit < 1000 ? limit : 1000
213
210
  } else {
@@ -252,7 +249,11 @@ export class Query {
252
249
  /**
253
250
  * 获取总数
254
251
  */
255
- public async count() {
252
+ public count(callback?: any) {
253
+ return withCallback(this._count(), callback)
254
+ }
255
+
256
+ private async _count() {
256
257
  interface Param {
257
258
  collectionName: string
258
259
  query?: Object
@@ -348,7 +349,6 @@ export class Query {
348
349
  const order = (this._apiOptions as QueryOption).order || {}
349
350
 
350
351
  const newApiOption = Object.assign({}, this._apiOptions, {
351
- // order: order.concat(newOrder)
352
352
  order: Object.assign({}, order, newOrder)
353
353
  })
354
354
 
@@ -390,7 +390,11 @@ export class Query {
390
390
  *
391
391
  * @param data 数据
392
392
  */
393
- public async update(data: Object): Promise<any> {
393
+ public update(data: Object, callback?: any): Promise<any> {
394
+ return withCallback(this._update(data), callback)
395
+ }
396
+
397
+ private async _update(data: Object): Promise<any> {
394
398
  if (!data || typeof data !== 'object') {
395
399
  return processReturn(this._db.config.throwOnCode, {
396
400
  ...ERRORS.INVALID_PARAM,
@@ -419,6 +423,10 @@ export class Query {
419
423
  data: UpdateSerializer.encodeEJSON(data, this._apiOptions.raw || false)
420
424
  }
421
425
 
426
+ if (this._transactionId) {
427
+ param.transactionId = this._transactionId
428
+ }
429
+
422
430
  if (this._fieldFilters) {
423
431
  param.query = this._fieldFilters
424
432
  }
@@ -481,7 +489,11 @@ export class Query {
481
489
  /**
482
490
  * 条件删除文档
483
491
  */
484
- public async remove() {
492
+ public remove(callback?: any) {
493
+ return withCallback(this._remove(), callback)
494
+ }
495
+
496
+ private async _remove() {
485
497
  // if (Object.keys(this._queryOptions).length > 0) {
486
498
  // console.warn('`offset`, `limit` and `projection` are not supported in remove() operation')
487
499
  // }
@@ -504,12 +516,15 @@ export class Query {
504
516
  let { multiple } = this._apiOptions as UpdateOption
505
517
  const multi = multiple === undefined ? true : multiple // where remove 不传multi默认为true
506
518
 
507
- const param = {
519
+ const param: any = {
508
520
  collectionName: this._coll,
509
521
  query: this._fieldFilters,
510
522
  queryType: QueryType.WHERE,
511
523
  multi
512
524
  }
525
+ if (this._transactionId) {
526
+ param.transactionId = this._transactionId
527
+ }
513
528
  const res = await this._request.send(
514
529
  'database.removeDocument',
515
530
  param,
@@ -579,33 +594,28 @@ export class Query {
579
594
  * 监听query对应的doc变化
580
595
  */
581
596
  watch = (options: IWatchOptions): DBRealtimeListener => {
582
- if (!Db.ws) {
583
- Db.ws = new RealtimeWebSocketClient({
584
- context: {
585
- appConfig: {
586
- docSizeLimit: 1000,
587
- realtimePingInterval: 10000,
588
- realtimePongWaitTimeout: 5000,
589
- request: this._request
590
- }
591
- }
592
- })
593
- }
597
+ const ws = getWsInstance(this._db)
594
598
 
595
599
  const { limit, order } = this._apiOptions as QueryOption
596
600
 
597
- return (Db.ws as RealtimeWebSocketClient).watch({
601
+ // order 由 orderBy() 存为 { field: 1 | -1 },实时推送协议要求 'asc' | 'desc'
602
+ const orderBy = order
603
+ ? Object.entries(order).reduce<Record<string, string>>((acc, [field, direction]) => {
604
+ acc[field] = direction === -1 ? 'desc' : 'asc'
605
+ return acc
606
+ }, {})
607
+ : undefined
608
+
609
+ return ws.watch({
598
610
  ...options,
599
611
  envId: this._db.config.env,
600
612
  collectionName: this._coll,
601
- query: JSON.stringify(this._fieldFilters), // 实时推送这里需要换成ejson协议,todo
613
+ // where() 产出 canonical EJSON 字符串,但 watch 协议要求 legacy 内部编码
614
+ // (数字裸值、$date 为 epoch 毫秒数字)——canonical 形态服务端虽接受但会静默匹配不到文档,
615
+ // 且不可再 JSON.stringify(否则双重编码,线上 100003 事故根因)
616
+ query: toWatchQuery(this._fieldFilters),
602
617
  limit,
603
- orderBy: order
604
- ? order.reduce<Record<string, string>>((acc, cur) => {
605
- acc[cur.field] = cur.direction
606
- return acc
607
- }, {})
608
- : undefined
618
+ orderBy
609
619
  })
610
620
  }
611
621
  }
@@ -4,15 +4,27 @@ import {
4
4
  QUERY_COMMANDS_LITERAL,
5
5
  QueryCommand
6
6
  } from '../commands/query'
7
- import { isLogicCommand, LOGIC_COMMANDS_LITERAL, LogicCommand } from '../commands/logic'
7
+ import {
8
+ isLogicCommand,
9
+ LOGIC_COMMANDS_LITERAL,
10
+ LogicCommand
11
+ } from '../commands/logic'
8
12
  import { SYMBOL_UNSET_FIELD_NAME, SYMBOL_GEO_POINT } from '../helper/symbol'
9
13
  import { getType, isObject, isArray, isRegExp, isDate } from '../utils/type'
10
14
  import { operatorToString } from '../operator-map'
11
- import { flattenQueryObject, isConversionRequired, encodeInternalDataType } from './common'
12
- import { IGeoNearOptions, IGeoWithinOptions, IGeoIntersectsOptions } from '../commands/query'
15
+ import {
16
+ flattenQueryObject,
17
+ isConversionRequired,
18
+ encodeInternalDataType
19
+ } from './common'
20
+ import {
21
+ IGeoNearOptions,
22
+ IGeoWithinOptions,
23
+ IGeoIntersectsOptions
24
+ } from '../commands/query'
13
25
  import { stringifyByEJSON } from '../utils/utils'
14
26
  import { Validate } from '../validate'
15
- import { Point } from '../geo'
27
+ import { Point } from '../geo/index'
16
28
 
17
29
  export type IQueryCondition = Record<string, any> | LogicCommand
18
30
 
@@ -39,10 +51,10 @@ class QueryEncoder {
39
51
  } else if (isQueryCommand(query)) {
40
52
  return this.encodeQueryCommand(query)
41
53
  } else if (isRegExp(query)) {
42
- // /xxx/ 形式
54
+ // /xxx/ 形式
43
55
  return { [key]: this.encodeRegExp(query) }
44
56
  } else if (isDate(query)) {
45
- return { [key]: query }
57
+ return { [key]: encodeInternalDataType(query) }
46
58
  } else {
47
59
  return { [key]: this.encodeQueryObject(query) }
48
60
  }
@@ -27,7 +27,7 @@ export class Transaction {
27
27
 
28
28
  public constructor(db: Db) {
29
29
  this._db = db
30
- this._request = new Db.reqClass(this._db.config)
30
+ this._request = Db.createRequest(this._db.config)
31
31
  this.aborted = false
32
32
  this.commited = false
33
33
  this.inited = false
@@ -1,10 +1,6 @@
1
1
  /**
2
2
  * Common interfaces and types
3
3
  */
4
-
5
- /**
6
- * Database Watch
7
- */
8
4
  import { Point } from '../geo/index'
9
5
 
10
6
  export type DataType = 'init' | 'update' | 'add' | 'remove' | 'replace' | 'limit'
@@ -1,9 +1,29 @@
1
+ import { Db } from '../'
1
2
  import { QueryOption, UpdateOption } from '../query'
2
3
  import { EJSON } from 'bson'
3
4
  import { isObject } from './type'
4
5
 
5
6
  export const sleep = (ms: number = 0) => new Promise(r => setTimeout(r, ms))
6
7
 
8
+ /**
9
+ * 兼容旧版 callback 调用风格(对齐 0.12.x 行为):
10
+ * - 传入 callback:成功时 callback(0, result)、失败时 callback(err),方法返回 undefined
11
+ * - 不传 callback:原样返回 promise
12
+ *
13
+ * 注意用 then(f, g) 而非 then(f).catch(g),保证派生 promise 为 resolved,
14
+ * 避免 callback 模式下产生 unhandled rejection。
15
+ */
16
+ export function withCallback<T>(promise: Promise<T>, callback?: any): Promise<T> | undefined {
17
+ if (typeof callback === 'function') {
18
+ promise.then(
19
+ (res: any) => callback(0, res),
20
+ (err: any) => callback(err)
21
+ )
22
+ return undefined
23
+ }
24
+ return promise
25
+ }
26
+
7
27
  const counters: Record<string, number> = {}
8
28
 
9
29
  export const autoCount = (domain: string = 'any'): number => {
@@ -13,6 +33,32 @@ export const autoCount = (domain: string = 'any'): number => {
13
33
  return counters[domain]++
14
34
  }
15
35
 
36
+ /**
37
+ * 工厂模式创建 ws 实例
38
+ */
39
+ const wsList = {}
40
+ export function getWsInstance(db: Db) {
41
+ if(!Db.wsClientClass){
42
+ throw new Error('to use realtime you must import realtime module first')
43
+ }
44
+ const { env } = db.config
45
+
46
+ if (!wsList[env]) {
47
+ wsList[env] = new Db.wsClientClass({
48
+ context: {
49
+ appConfig: {
50
+ docSizeLimit: 1000,
51
+ realtimePingInterval: 10000,
52
+ realtimePongWaitTimeout: 5000,
53
+ request: Db.createRequest(db.config)
54
+ }
55
+ }
56
+ })
57
+ }
58
+
59
+ return wsList[env]
60
+ }
61
+
16
62
  export const getReqOpts = (apiOptions: QueryOption | UpdateOption): any => {
17
63
  // 影响底层request的暂时只有timeout
18
64
  if (apiOptions.timeout !== undefined) {
@@ -53,6 +99,65 @@ export const parseByEJSON = params => {
53
99
  return EJSON.parse(params)
54
100
  }
55
101
 
102
+ /**
103
+ * 将 EJSON 值递归转为实时推送 watch 协议要求的 legacy 内部编码(0.12.x 形态)
104
+ * - Date → { $date: <epoch 毫秒数字> }
105
+ * - ObjectId → { $oid }、BSONRegExp/原生 RegExp → { $regex, $options }
106
+ * - 数字/字符串/布尔等基本值保持裸值
107
+ *
108
+ * bson 类型用 _bsontype 字符串判断而非 instanceof:EJSON.parse 产出的实例
109
+ * 来自运行时实际加载的 bson(可能与编译时版本/副本不同),instanceOf 跨副本
110
+ * 会失效,_bsontype 是 bson 各版本稳定的类型标记。
111
+ */
112
+ const toWatchLegacyValue = (val: any): any => {
113
+ if (val instanceof Date) {
114
+ return { $date: +val }
115
+ }
116
+ if (val instanceof RegExp) {
117
+ // 原生 RegExp 兜底(正常链路编码器已转 $regularExpression,raw 模式等可能漏过)
118
+ return { $regex: val.source, $options: val.flags }
119
+ }
120
+ if (val && typeof val === 'object') {
121
+ if (val._bsontype === 'ObjectId') {
122
+ return { $oid: val.toHexString() }
123
+ }
124
+ if (val._bsontype === 'BSONRegExp') {
125
+ return { $regex: val.pattern, $options: val.options }
126
+ }
127
+ if (Array.isArray(val)) {
128
+ return val.map(toWatchLegacyValue)
129
+ }
130
+ const ret: Record<string, any> = {}
131
+ for (const key of Object.keys(val)) {
132
+ ret[key] = toWatchLegacyValue(val[key])
133
+ }
134
+ return ret
135
+ }
136
+ return val
137
+ }
138
+
139
+ /**
140
+ * 实时推送 watch 协议的 query 编码
141
+ *
142
+ * where() 产出的 _fieldFilters 是 canonical EJSON 字符串(relaxed:false),
143
+ * 但服务端 watch 查询引擎只认 0.12.x 的 legacy 内部编码:
144
+ * - 数字必须裸值({$numberInt}/{$numberLong} 会被静默匹配不到文档,实测 0 命中)
145
+ * - 日期必须是 { $date: <epoch 毫秒数字> }(ISO 串与 {$date:{$numberLong}} 均不命中)
146
+ * 此处 parse 后转成 legacy 形态再序列化。
147
+ */
148
+ export const toWatchQuery = (ejsonStr: string): string => {
149
+ if (!ejsonStr) {
150
+ return '{}'
151
+ }
152
+ try {
153
+ const parsed = EJSON.parse(ejsonStr)
154
+ return JSON.stringify(toWatchLegacyValue(parsed))
155
+ } catch (e) {
156
+ // 非 EJSON 文本(EJSON.parse 兼容普通 JSON,走到这说明确实解析不了),原样透传由调用方保证格式
157
+ return ejsonStr
158
+ }
159
+ }
160
+
56
161
  export class TcbError extends Error {
57
162
  readonly code: string
58
163
  readonly message: string
package/src/validate.ts CHANGED
@@ -11,9 +11,8 @@ import { QueryOption, UpdateOption } from './query'
11
11
  import { ERRORS } from './const/code'
12
12
  import { E } from './utils/utils'
13
13
  import { getType } from './utils/type'
14
- import { Point } from './geo'
14
+ import { Point } from './geo/index'
15
15
  import { CenterSphere } from './typings'
16
-
17
16
  import { SYMBOL_GEO_POINT } from './helper/symbol'
18
17
 
19
18
  const validOptionsKeys = ['limit', 'offset', 'projection', 'order', 'multiple', 'timeout', 'raw']
@@ -243,11 +242,9 @@ export class Validate {
243
242
  }
244
243
 
245
244
  /**
245
+ * 校验 centerSphere 结构
246
246
  *
247
- * @static
248
- * @param {[Point, number]} param
249
- * @returns {Boolean}
250
- * @memberof Validate
247
+ * @param param - [Point, number] 或 [[number, number], number]
251
248
  */
252
249
  static isCentersPhere(param: CenterSphere): Boolean {
253
250
  if (Array.isArray(param) && param.length === 2) {