1 |
|
2 |
|
3 | xtend = require("xtend")
|
4 | AbstractObject = require("abstract-object")
|
5 | Codec = require("buffer-codec")
|
6 | utf8ByteLength = Codec.getByteLen
|
7 | Errors = require("./abstract-error")
|
8 | try AbstractIterator = require("abstract-iterator")
|
9 | AbstractChainedBatch = require("./abstract-chained-batch")
|
10 | setImmediate = global.setImmediate or process.nextTick
|
11 |
|
12 | AbstractError = Errors.AbstractError
|
13 | NotImplementedError = Errors.NotImplementedError
|
14 | InvalidArgumentError = Errors.InvalidArgumentError
|
15 | OpenError = Errors.OpenError
|
16 | CloseError = Errors.CloseError
|
17 | inherits = require("abstract-object/lib/util/inherits")
|
18 | isString = require("abstract-object/lib/util/isString")
|
19 | isFunction = require("abstract-object/lib/util/isFunction")
|
20 | isArray = require("abstract-object/lib/util/isArray")
|
21 |
|
22 | module.exports = class AbstractNoSQL
|
23 | inherits AbstractNoSQL, AbstractObject
|
24 |
|
25 | constructor: ->
|
26 | super
|
27 | initialize:(location) ->
|
28 |
|
29 | throw new InvalidArgumentError("constructor requires a location string argument") if location and typeof location isnt "string"
|
30 | @location = location
|
31 | finalize: ->
|
32 | if @_opened
|
33 | if @_closeSync then @closeSync()
|
34 | else @closeAsync()
|
35 | @_options = null
|
36 |
|
37 | @::__defineGetter__ "opened", ->
|
38 | !!@_opened
|
39 |
|
40 | setOpened: (aValue, options)->
|
41 | if aValue
|
42 | @_opened = true
|
43 | @_options = options if options
|
44 | @emit "ready"
|
45 | @emit "open"
|
46 | else
|
47 | @_opened = false
|
48 | @emit "closed"
|
49 |
|
50 |
|
51 | isExistsSync: (key, options) ->
|
52 | options = {} unless options?
|
53 | key = String(key) unless @_isBuffer(key)
|
54 | if @_isExistsSync
|
55 | result = @_isExistsSync(key, options)
|
56 | return result
|
57 | else if @_getSync
|
58 | try
|
59 | @_getSync key, options
|
60 | return true
|
61 | catch err
|
62 | if AbstractError.isNotFound(err)
|
63 | return false
|
64 | else
|
65 | throw err
|
66 | throw new NotImplementedError()
|
67 | isExistSync: @::isExistsSync
|
68 |
|
69 | getSync: (key, options) ->
|
70 | if @_getSync
|
71 | options = {} unless options?
|
72 | throw err if err = @_checkKey(key, "key")
|
73 | key = String(key) unless @_isBuffer(key)
|
74 | result = @_getSync(key, options)
|
75 | return result
|
76 | throw new NotImplementedError()
|
77 |
|
78 | getBufferSync: (key, destBuffer, options) ->
|
79 | if @_getBufferSync
|
80 | options = {} unless options?
|
81 | options.offset = 0 unless options.offset?
|
82 | result = @_getBufferSync(key, destBuffer, options)
|
83 | return result
|
84 | throw new NotImplementedError()
|
85 |
|
86 | mGetSync: (keys, options) ->
|
87 | if @_mGetSync
|
88 | options = {} unless options?
|
89 | options.raiseError = options.raiseError isnt false
|
90 | needKeyName = options.keys
|
91 | arr = @_mGetSync(keys, options)
|
92 | i = 0
|
93 | result = []
|
94 | while i < arr.length
|
95 | if needKeyName isnt false
|
96 | result.push
|
97 | key: arr[i]
|
98 | value: arr[++i]
|
99 | else
|
100 | result.push arr[i]
|
101 | i++
|
102 | return result
|
103 | throw new NotImplementedError()
|
104 |
|
105 | putSync: (key, value, options) ->
|
106 | if @_putSync
|
107 | options = {} unless options?
|
108 | result = @_putSync(key, value, options)
|
109 | return result
|
110 | throw new NotImplementedError()
|
111 |
|
112 | delSync: (key, options) ->
|
113 | if @_delSync
|
114 | options = {} unless options?
|
115 | result = @_delSync(key, options)
|
116 | return result
|
117 | throw new NotImplementedError()
|
118 |
|
119 | batchSync: (operations, options) ->
|
120 | if @_batchSync
|
121 | options = {} unless options?
|
122 | unless isArray(operations)
|
123 | throw new InvalidArgumentError("batch(operations) requires an array argument")
|
124 | for e in operations
|
125 | continue unless typeof e is "object"
|
126 | throw err if err = @_checkKey(e.type, "type")
|
127 | throw err if err = @_checkKey(e.key, "key")
|
128 | result = @_batchSync(operations, options)
|
129 | return result
|
130 | throw new NotImplementedError()
|
131 |
|
132 | approximateSizeSync: (start, end) ->
|
133 | if @_approximateSizeSync
|
134 | if not start? or not end?
|
135 | throw new InvalidArgumentError("approximateSize() requires valid `start`, `end` arguments")
|
136 | start = String(start) unless @_isBuffer(start)
|
137 | end = String(end) unless @_isBuffer(end)
|
138 | result = @_approximateSizeSync(start, end)
|
139 | return result
|
140 | throw new NotImplementedError()
|
141 |
|
142 | openSync: (options) ->
|
143 | if @_openSync
|
144 | options = @_options || {} unless options?
|
145 | options.createIfMissing = options.createIfMissing isnt false
|
146 | options.errorIfExists = !!options.errorIfExists
|
147 | @emit "opening", options
|
148 | result = @_openSync(options)
|
149 | @setOpened true, options if result
|
150 | result = @ if result
|
151 | return result
|
152 | throw new NotImplementedError()
|
153 |
|
154 |
|
155 |
|
156 | closeSync: ->
|
157 | if @_closeSync
|
158 | @emit "closing"
|
159 | result = @_closeSync()
|
160 | @setOpened false if result
|
161 | return result
|
162 | throw new NotImplementedError()
|
163 |
|
164 |
|
165 |
|
166 |
|
167 | _open: (options, callback) ->
|
168 | that = this
|
169 | if @_openSync
|
170 | setImmediate ->
|
171 | result = undefined
|
172 | try
|
173 | result = that._openSync(options)
|
174 | catch err
|
175 | callback err
|
176 | return
|
177 | if result
|
178 | callback null, result
|
179 | else
|
180 | callback new OpenError("can not open database.")
|
181 |
|
182 | else
|
183 | setImmediate callback
|
184 |
|
185 | _close: (callback) ->
|
186 | that = this
|
187 | if @_closeSync
|
188 | setImmediate ->
|
189 | result = undefined
|
190 | try
|
191 | result = that._closeSync()
|
192 | catch err
|
193 | callback err
|
194 | return
|
195 | if result
|
196 | callback null, result
|
197 | else
|
198 | callback new CloseError("can not close database.")
|
199 |
|
200 | else
|
201 | setImmediate callback
|
202 |
|
203 | _isExists: (key, options, callback) ->
|
204 | that = this
|
205 | if @_isExistsSync
|
206 | setImmediate ->
|
207 | result = undefined
|
208 | try
|
209 | result = that._isExistsSync(key, options)
|
210 | catch err
|
211 | callback err
|
212 | return
|
213 | callback null, result
|
214 |
|
215 | else
|
216 | @_get key, options, (err, value) ->
|
217 | if err
|
218 | if AbstractError.isNotFound(err)
|
219 | callback null, false
|
220 | else
|
221 | callback err
|
222 | else
|
223 | callback null, true
|
224 |
|
225 | _getBuffer: (key, destBuffer, options, callback) ->
|
226 | that = this
|
227 | if @_getSync or @_getBufferSync isnt AbstractNoSQL::_getBufferSync
|
228 | setImmediate ->
|
229 | result = undefined
|
230 | try
|
231 | result = that._getBufferSync(key, destBuffer, options)
|
232 | catch err
|
233 | callback err
|
234 | return
|
235 | callback null, result
|
236 | else if @_get
|
237 | @_get key, options, (err, value)->
|
238 | return callback(err) if err
|
239 | result = utf8ByteLength(value)
|
240 | if destBuffer
|
241 | result = Math.min(result, destBuffer.length)
|
242 | result = destBuffer.write(value, options.offset, result) if result
|
243 | callback null, result
|
244 | else
|
245 | setImmediate callback
|
246 |
|
247 | _getBufferSync: (key, destBuffer, options) ->
|
248 | if @_getSync
|
249 | value = @_getSync(key, options)
|
250 | result = utf8ByteLength(value)
|
251 | if destBuffer
|
252 | result = Math.min(result, destBuffer.length)
|
253 | result = destBuffer.write(value, options.offset, result) if result
|
254 | return result
|
255 | else
|
256 | throw new NotImplementedError('_mGetSync: _getSync is not implemented.')
|
257 |
|
258 | _mGetSync: (keys, options) ->
|
259 | if @_getSync
|
260 | result = []
|
261 | needKeyName = options.keys
|
262 | raiseError = options.raiseError
|
263 | options.asBuffer = options.asBuffer is true
|
264 | for key in keys
|
265 | try
|
266 | value = @_getSync(key, options)
|
267 | catch err
|
268 | throw err if raiseError
|
269 | value = undefined
|
270 | if needKeyName isnt false
|
271 | result.push key, value
|
272 | else
|
273 | result.push value
|
274 | return result
|
275 | else
|
276 | throw new NotImplementedError('_mGetSync: _getSync is not implemented.')
|
277 |
|
278 | _mGet: (keys, options, callback) ->
|
279 | that = this
|
280 | if @_getSync or @_mGetSync isnt AbstractNoSQL::_mGetSync
|
281 | setImmediate ->
|
282 | result = undefined
|
283 | try
|
284 | result = that._mGetSync keys, options
|
285 | catch err
|
286 | callback err
|
287 | return
|
288 | callback null, result
|
289 | else if keys.length > 0 and @_get
|
290 | result = []
|
291 | i = 0
|
292 | needKeyName = options.keys
|
293 | raiseError = options.raiseError
|
294 |
|
295 | readNext = (err, value)->
|
296 | return callback(err) if err and raiseError
|
297 |
|
298 | if needKeyName isnt false
|
299 | result.push keys[i], value
|
300 | else
|
301 | result.push value
|
302 | i++
|
303 | return callback(null, result) if i >= keys.length
|
304 | that._get keys[i], options, readNext
|
305 | @_get keys[i], options, readNext
|
306 | else
|
307 | setImmediate callback
|
308 |
|
309 | _get: (key, options, callback) ->
|
310 | that = this
|
311 | if @_getSync
|
312 | setImmediate ->
|
313 | result = undefined
|
314 | try
|
315 | result = that._getSync(key, options)
|
316 | catch err
|
317 | callback err
|
318 | return
|
319 | callback null, result
|
320 |
|
321 | else
|
322 | setImmediate callback
|
323 |
|
324 | _put: (key, value, options, callback) ->
|
325 | that = this
|
326 | if @_putSync
|
327 | setImmediate ->
|
328 | result = undefined
|
329 | try
|
330 | result = that._putSync(key, value, options)
|
331 | catch err
|
332 | callback err
|
333 | return
|
334 | callback null, result
|
335 |
|
336 | else
|
337 | setImmediate callback
|
338 |
|
339 | _del: (key, options, callback) ->
|
340 | that = this
|
341 | if @_delSync
|
342 | setImmediate ->
|
343 | result = undefined
|
344 | try
|
345 | result = that._delSync(key, options)
|
346 | catch err
|
347 | callback err
|
348 | return
|
349 | callback null, result
|
350 |
|
351 | else
|
352 | setImmediate callback
|
353 |
|
354 | _batch: (array, options, callback) ->
|
355 | that = this
|
356 | if @_batchSync
|
357 | setImmediate ->
|
358 | result = undefined
|
359 | try
|
360 | result = that._batchSync(array, options)
|
361 | catch err
|
362 | callback err
|
363 | return
|
364 | callback null, result
|
365 |
|
366 | else
|
367 | setImmediate callback
|
368 |
|
369 |
|
370 |
|
371 | _approximateSize: (start, end, callback) ->
|
372 | that = this
|
373 | if @_approximateSizeSync
|
374 | setImmediate ->
|
375 | result = undefined
|
376 | try
|
377 | result = that._approximateSizeSync(start, end)
|
378 | catch err
|
379 | callback err
|
380 | return
|
381 | callback null, result
|
382 | else
|
383 | setImmediate callback
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 |
|
406 | openAsync: (options, callback) ->
|
407 | options = {} unless options?
|
408 | options.createIfMissing = options.createIfMissing isnt false
|
409 | options.errorIfExists = !!options.errorIfExists
|
410 | that = this
|
411 | @emit "opening", options
|
412 | @_open options, (err, result) ->
|
413 | that.setOpened true, options if not err?
|
414 | callback err, result
|
415 | open: (options, callback) ->
|
416 | if isFunction options
|
417 | callback = options
|
418 | options = undefined
|
419 | if callback
|
420 | @openAsync options, callback
|
421 | else
|
422 | @openSync options
|
423 |
|
424 | closeAsync: (callback) ->
|
425 | that = this
|
426 | callback = undefined unless isFunction callback
|
427 | @emit "closing"
|
428 | @_close (err, result) ->
|
429 | return that.dispatchError err, callback if err
|
430 | that.setOpened false
|
431 | callback null, result if callback
|
432 | close: (callback) ->
|
433 | if callback
|
434 | @closeAsync callback
|
435 | else
|
436 | @closeSync()
|
437 |
|
438 | isExistsAsync: (key, options, callback) ->
|
439 | options = {} unless options?
|
440 | key = String(key) unless @_isBuffer(key)
|
441 | @_isExists key, options, callback
|
442 | isExists: (key, options, callback) ->
|
443 | if isFunction options
|
444 | callback = options
|
445 | options = {}
|
446 | else
|
447 | if callback
|
448 | @isExistsAsync key, options, callback
|
449 | else
|
450 | @isExistsSync key, options
|
451 | isExist: @::isExists
|
452 |
|
453 | getBufferAsync: (key, destBuffer, options, callback) ->
|
454 | options = {} unless options?
|
455 | options.offset = 0 unless options.offset?
|
456 | @_getBuffer key, destBuffer, options, callback
|
457 | getBuffer: (key, destBuffer, options, callback) ->
|
458 | err = undefined
|
459 | if isFunction options
|
460 | callback = options
|
461 | options = {}
|
462 | if callback
|
463 | @getBufferAsync key, destBuffer, options, callback
|
464 | else
|
465 | @getBufferSync key, destBuffer, options
|
466 |
|
467 | mGetAsync: (keys, options, callback) ->
|
468 | options = {} unless options?
|
469 | options.asBuffer = options.asBuffer is true
|
470 | options.raiseError = options.raiseError isnt false
|
471 | needKeyName = options.keys isnt false
|
472 | @_mGet keys, options, (err, arr)->
|
473 | return callback(err) if err
|
474 | if needKeyName
|
475 | i = 0
|
476 | result = []
|
477 | while i < arr.length
|
478 | result.push
|
479 | key: arr[i]
|
480 | value: arr[++i]
|
481 | i++
|
482 | else
|
483 | result = arr
|
484 | callback null, result
|
485 | mGet: (keys, options, callback) ->
|
486 | err = undefined
|
487 | if isFunction options
|
488 | callback = options
|
489 | options = {}
|
490 | else
|
491 | if callback
|
492 | @mGetAsync keys, options, callback
|
493 | else
|
494 | @mGetSync keys, options
|
495 |
|
496 | getAsync: (key, options, callback) ->
|
497 | options = {} unless options?
|
498 | return callback(err) if err = @_checkKey(key, "key")
|
499 | key = String(key) unless @_isBuffer(key)
|
500 | options.asBuffer = options.asBuffer is true
|
501 | @_get key, options, callback
|
502 | get: (key, options, callback) ->
|
503 | err = undefined
|
504 | if isFunction options
|
505 | callback = options
|
506 | options = {}
|
507 | if callback
|
508 | @getAsync key, options, callback
|
509 | else
|
510 | @getSync key, options
|
511 |
|
512 | putAsync: (key, value, options, callback) ->
|
513 | options = {} unless options?
|
514 | return callback(err) if err = @_checkKey(key, "key", @_isBuffer)
|
515 | key = String(key) unless @_isBuffer(key)
|
516 |
|
517 |
|
518 | value = String(value) if value? and not @_isBuffer(value) and not process.browser
|
519 | @_put key, value, options, callback
|
520 |
|
521 | put: (key, value, options, callback) ->
|
522 | err = undefined
|
523 | if isFunction options
|
524 | callback = options
|
525 | options = {}
|
526 | if callback
|
527 | @putAsync key, value, options, callback
|
528 | else
|
529 | @putSync key, value, options
|
530 |
|
531 | delAsync: (key, options, callback) ->
|
532 | options = {} unless options?
|
533 | return callback(err) if err = @_checkKey(key, "key", @_isBuffer)
|
534 | key = String(key) unless @_isBuffer(key)
|
535 | @_del key, options, callback
|
536 | del: (key, options, callback) ->
|
537 | err = undefined
|
538 | if isFunction options
|
539 | callback = options
|
540 | options = {}
|
541 | if callback
|
542 | @delAsync key, options, callback
|
543 | else
|
544 | @delSync key, options
|
545 |
|
546 | batchAsync: (array, options, callback) ->
|
547 | options = {} unless options?
|
548 | unless isArray(array)
|
549 | vError = new InvalidArgumentError("batch(array) requires an array argument")
|
550 | return callback(vError)
|
551 | for e in array
|
552 | continue unless typeof e is "object"
|
553 | return callback(err) if err = @_checkKey(e.type, "type")
|
554 | return callback(err) if err = @_checkKey(e.key, "key")
|
555 | @_batch array, options, callback
|
556 | batch: (array, options, callback) ->
|
557 | return @_chainedBatch() unless arguments.length
|
558 | if isFunction options
|
559 | callback = options
|
560 | options = {}
|
561 | callback = array if isFunction array
|
562 | if callback
|
563 | @batchAsync array, options, callback
|
564 | else
|
565 | @batchSync array, options
|
566 |
|
567 |
|
568 |
|
569 | approximateSizeAsync: (start, end, callback) ->
|
570 | start = String(start) unless @_isBuffer(start)
|
571 | end = String(end) unless @_isBuffer(end)
|
572 | @_approximateSize start, end, callback
|
573 | approximateSize: (start, end, callback) ->
|
574 | if not start? or not end? or isFunction(start) or isFunction(end)
|
575 | throw new InvalidArgumentError("approximateSize() requires valid `start`, `end` and `callback`(for async) arguments")
|
576 | if callback
|
577 | @approximateSizeAsync start, end, callback
|
578 | else
|
579 | @approximateSizeSync start, end
|
580 |
|
581 |
|
582 | IteratorClass: AbstractIterator
|
583 | iterator: (options) ->
|
584 | options = {} unless typeof options is "object"
|
585 | if @IteratorClass
|
586 | return new @IteratorClass(this, options)
|
587 | else if isFunction @_iterator
|
588 | console.error "_iterator is deprecated. please use the IteratorClass instead."
|
589 | return @_iterator(options)
|
590 | throw new NotImplementedError()
|
591 |
|
592 | _chainedBatch: ->
|
593 | new AbstractChainedBatch(this)
|
594 |
|
595 | _isBuffer: (obj) ->
|
596 | Buffer.isBuffer obj
|
597 |
|
598 | _checkKey: (obj, type) ->
|
599 | if not obj?
|
600 | return new InvalidArgumentError(type + " cannot be `null` or `undefined`")
|
601 | if @_isBuffer(obj)
|
602 | return new InvalidArgumentError(type + " cannot be an empty Buffer") if obj.length is 0
|
603 | else
|
604 | return new InvalidArgumentError(type + " cannot be an empty String") if String(obj) is ""
|
605 |
|
606 | isOpen: ->
|
607 | !!@_opened
|
608 |
|
609 | module.exports.AbstractNoSQL = AbstractNoSQL
|
610 | module.exports.__defineGetter__ "AbstractLevelDOWN", ->
|
611 | console.error "AbstractLevelDOWN is deprecated. use AbstractNoSQL instead."
|
612 | AbstractNoSQL
|
613 | module.exports.__defineGetter__ "AbstractIterator", ->
|
614 | console.error "AbstractIterator is deprecated. it's moved to abstract-iterator."
|
615 | console.error "first `npm install abstract-iterator`" unless AbstractIterator
|
616 | AbstractIterator
|
617 |
|
618 | module.exports.AbstractChainedBatch = AbstractChainedBatch
|