UNPKG

21.9 kBMarkdownView Raw
1# Abstract NoSQL Database [![Build Status](https://secure.travis-ci.org/snowyu/node-abstract-nosql.png)](http://travis-ci.org/snowyu/node-abstract-nosql)
2
3[![NPM](https://nodei.co/npm/abstract-nosql.png?downloads=true&downloadRank=true)](https://nodei.co/npm/abstract-nosql/)
4[![NPM](https://nodei.co/npm-dl/abstract-nosql.png?months=6&height=3)](https://nodei.co/npm/abstract-nosql/)
5
6
7Abstract-nosql package is modified from abstract-leveldown to enhance the synchronous methods supports for development a node nosql database quickly and using easily.
8
9abstract-nosql Interface is neutral. There is no bias neither synchronous bias nor asynchronous bias. So that more people choose according to their own manner. For myself, I am not very concerned about the performance of javascript, I am more concerned about the efficiency of its development, as well as through functional programming (functions, closures such a simple concept) extend out of the rich and wonderful world. I still can not help but to think about performance issues. Asynchronous itself produces a small gap, because javascript reason this gap is magnified.
10
11just saying that the asynchronous and synchronous consideration, if a function is only 1% of the opportunity to visit the IO, most of the time (99%) are in memory access. I want to different considerations, have different choices. And this decision is unlikely that done by the interface instead.
12
13Synchronous operation converts into asynchronous operation is easy, and almost no performance loss, in turn, may not. Conversion are many ways, setImmediate is not the best, but it is the simplest one.
14ES6 generator or [node-fibers](https://github.com/laverdet/node-fibers) could be a better way. the coroutine/fiber is lighter and more efficient than thread.
15
16The setImmediate package could be extended to use different implementation(setImmediate, nextTick, ES6 generator, node-fiber) in different environment.
17So the simulated asynchronous uses this way, if you do not implement the asynchronous methods.
18
19## About LevelDOWN
20
21An abstract prototype matching the **[LevelDOWN](https://github.com/rvagg/node-leveldown/)** API. Useful for extending **[LevelUP](https://github.com/rvagg/node-levelup)** functionality by providing a replacement to LevelDOWN.
22
23As of version 0.7, LevelUP allows you to pass a `'db'` option when you create a new instance. This will override the default LevelDOWN store with a LevelDOWN API compatible object.
24
25**Abstract LevelDOWN** provides a simple, operational *noop* base prototype that's ready for extending. By default, all operations have sensible "noops" (operations that essentially do nothing). For example, simple operations such as `.open(callback)` and `.close(callback)` will simply invoke the callback (on a *next tick*). More complex operations perform sensible actions, for example: `.get(key, callback)` will always return a `'NotFound'` `Error` on the callback.
26
27You add functionality by implementing the underscore versions of the operations. For example, to implement a `put()` operation you add a `_put()` method to your object. Each of these underscore methods override the default *noop* operations and are always provided with **consistent arguments**, regardless of what is passed in by the client.
28
29Additionally, all methods provide argument checking and sensible defaults for optional arguments. All bad-argument errors are compatible with LevelDOWN (they pass the LevelDOWN method arguments tests). For example, if you call `.open()` without a callback argument you'll get an `Error('open() requires a callback argument')`. Where optional arguments are involved, your underscore methods will receive sensible defaults. A `.get(key, callback)` will pass through to a `._get(key, options, callback)` where the `options` argument is an empty object.
30
31
32## Changes(diference from abstract-leveldown)
33
34+ !TODO: Add the stream ability
35+ Add the AbstractError and error code supports.
36* DB constructor allows no location.
37* Add IteratorClass supports.
38+ Add synchronous methods supports.
39 * Add the synchronous methods support now. You can implement the synchronous methods only.
40 * The asynchronous methods will be simulated via these synchronous methods. If you wanna
41 * support the asynchronous methods only, just do not implement these synchronous methods.
42 * But if you wanna support the synchronous only, you should override the asynchronous methods to disable it.
43+ isExists/isExistsSync optional method to test key whether exists.
44 * it will use the \_get/\_getSync method if no \_isExists or \_isExistsSync implemented
45+ the AbstractNoSQL class supports events now.
46 * emit `'open'` and `'ready'` event after the database is opened.
47 * emit `'closed'` event after the database is closed.
48+ isOpen()/opened to test the database whether opened.
49
50## AbstractError Classes
51
52see [abstract-object](https://github.com/snowyu/abstract-object)
53
54### AbstractError
55
56All Errors are derived from the AbstractError.
57
58* Members:
59 * message: the error message.
60 * code: the error code.
61* Methods:
62 * ok()
63 * notFound()
64 * ....
65 * invalidFormat()
66* Class Methods:
67 * AbstractError.isOk(err)
68 * AbstractError.isNotFound(err)
69 * ...
70
71the error codes:
72
73* AbstractError.Ok = 0
74* AbstractError.NotFound = 1
75* AbstractError.Corruption = 2
76* AbstractError.NotSupported = 3
77* AbstractError.InvalidArgument = 4
78* AbstractError.IO = 5
79* AbstractError.NotOpened = 6
80* AbstractError.InvalidType = 7
81* AbstractError.InvalidFormat = 8
82
83
84### Other Error Classes:
85
86* NotFoundError
87* CorruptionError
88* NotSupportedError/NotImplementedError
89* InvalidArgumentError
90* IOError
91* NotOpenedError
92* InvalidTypeError
93* InvalidFormatError
94* OpenError
95* CloseError
96* AlreadyEndError
97
98
99```js
100var OpenError = createError("CanNotOpen", 51)
101var CloseError = createError("CanNotClose", 52)
102var AlreadyEndError = createError("AlreadyEnd", 53)
103```
104
105
106## Example
107
108A simplistic in-memory LevelDOWN replacement
109
110use sync methods:
111
112
113```js
114var util = require('util')
115 , AbstractLevelDOWN = require('./').AbstractLevelDOWN
116
117// constructor, passes through the 'location' argument to the AbstractLevelDOWN constructor
118function FakeLevelDOWN (location) {
119 AbstractLevelDOWN.call(this, location)
120}
121
122// our new prototype inherits from AbstractLevelDOWN
123util.inherits(FakeLevelDOWN, AbstractLevelDOWN)
124
125// implement some methods
126
127FakeLevelDOWN.prototype._openSync = function (options) {
128 this._store = {}
129 return true
130}
131
132FakeLevelDOWN.prototype._putSync = function (key, value, options) {
133 key = '_' + key // safety, to avoid key='__proto__'-type skullduggery
134 this._store[key] = value
135 return true
136}
137
138//the isExists is an optional method:
139FakeLevelDOWN.prototype._isExistsSync = function (key, options) {
140 return this._store.hasOwnProperty('_' + key)
141}
142
143FakeLevelDOWN.prototype._getSync = function (key, options) {
144 var value = this._store['_' + key]
145 if (value === undefined) {
146 // 'NotFound' error, consistent with LevelDOWN API
147 throw new Error('NotFound')
148 }
149 return value
150}
151
152FakeLevelDOWN.prototype._delSync = function (key, options) {
153 delete this._store['_' + key]
154 return true
155}
156
157//use it directly
158
159var db = new FakeLevelDOWN()
160
161//sync:
162db.put('foo', 'bar')
163var result = db.get('foo')
164
165//async:
166db.put('foo', 'bar', function (err) {
167 if (err) throw err
168 db.get('foo', function (err, value) {
169 if (err) throw err
170 console.log('Got foo =', value)
171 db.isExists('foo', function(err, isExists){
172 if (err) throw err
173 console.log('isExists foo =', isExists)
174 })
175 })
176})
177
178//stream:
179
180db.readStream().on('data', function(data){
181})
182
183// now use it in LevelUP
184
185var levelup = require('levelup')
186
187var db = levelup('/who/cares/', {
188 // the 'db' option replaces LevelDOWN
189 db: function (location) { return new FakeLevelDOWN(location) }
190})
191
192//async:
193db.put('foo', 'bar', function (err) {
194 if (err) throw err
195 db.get('foo', function (err, value) {
196 if (err) throw err
197 console.log('Got foo =', value)
198 db.isExists('foo', function(err, isExists){
199 if (err) throw err
200 console.log('isExists foo =', isExists)
201 })
202 })
203})
204
205//sync:
206db.put('foo', 'bar')
207console.log(db.get('foo'))
208console.log(db.isExists('foo'))
209```
210
211use async methods(no sync supports):
212
213
214```js
215var util = require('util')
216 , AbstractLevelDOWN = require('./').AbstractLevelDOWN
217
218// constructor, passes through the 'location' argument to the AbstractLevelDOWN constructor
219function FakeLevelDOWN (location) {
220 AbstractLevelDOWN.call(this, location)
221}
222
223// our new prototype inherits from AbstractLevelDOWN
224util.inherits(FakeLevelDOWN, AbstractLevelDOWN)
225
226// implement some methods
227
228FakeLevelDOWN.prototype._open = function (options, callback) {
229 // initialise a memory storage object
230 this._store = {}
231 // optional use of nextTick to be a nice async citizen
232 process.nextTick(function () { callback(null, this) }.bind(this))
233}
234
235FakeLevelDOWN.prototype._put = function (key, value, options, callback) {
236 key = '_' + key // safety, to avoid key='__proto__'-type skullduggery
237 this._store[key] = value
238 process.nextTick(callback)
239}
240
241//the isExists is an optional method:
242FakeLevelDOWN.prototype._isExists = function (key, options, callback) {
243 var value = this._store.hasOwnProperty('_' + key)
244 process.nextTick(function () {
245 callback(null, value)
246 })
247}
248
249FakeLevelDOWN.prototype._get = function (key, options, callback) {
250 var value = this._store['_' + key]
251 if (value === undefined) {
252 // 'NotFound' error, consistent with LevelDOWN API
253 return process.nextTick(function () { callback(new Error('NotFound')) })
254 }
255 process.nextTick(function () {
256 callback(null, value)
257 })
258}
259
260FakeLevelDOWN.prototype._del = function (key, options, callback) {
261 delete this._store['_' + key]
262 process.nextTick(callback)
263}
264
265// now use it in LevelUP
266
267var levelup = require('levelup')
268
269var db = levelup('/who/cares/', {
270 // the 'db' option replaces LevelDOWN
271 db: function (location) { return new FakeLevelDOWN(location) }
272})
273
274db.put('foo', 'bar', function (err) {
275 if (err) throw err
276 db.get('foo', function (err, value) {
277 if (err) throw err
278 console.log('Got foo =', value)
279 })
280})
281```
282
283See [MemDOWN](https://github.com/rvagg/memdown/) if you are looking for a complete in-memory replacement for LevelDOWN.
284
285## Streamable
286
287Once implements the AbstractIterator.\_nextSync() or AbstractIterator.\_next().
288the db should be the streamable.
289
290
291
292### AbstractLevelDOWN.createReadStream
293### AbstractLevelDOWN.readStream
294
295create a readable stream.
296
297* AbstractLevelDOWN.readStream([options])
298* AbstractLevelDOWN.createReadStream
299
300__arguments__
301
302* options: the optional options object
303 * `'next'` *()*: the raw key data to ensure the readStream return keys is greater than the key. See `'last'` event.
304 * note: this will affect the range[gt/gte or lt/lte(reverse)] options.
305 * `'filter'` *(function)*: to filter data in the stream
306 * function filter(key, value) if return:
307 * 0(.FILTER_INCLUDED): include this item
308 * 1(.FILTER_EXCLUDED): exclude
309 * -1(.FILTER_STOPPED): stop stream.
310 * note: the filter function argument 'key' and 'value' may be null, it is affected via keys and values of this options.
311 * `'range'` *(string or array)*: the keys are in the give range as the following format:
312 * string:
313 * "[a, b]": from a to b. a,b included. this means {gte='a', lte = 'b'}
314 * "(a, b]": from a to b. b included, a excluded. this means {gt='a', lte='b'}
315 * "[, b)" from begining to b, begining included, b excluded. this means {lt='b'}
316 * note: this will affect the gt/gte/lt/lte options.
317 * array: the key list to get. eg, ['a', 'b', 'c']
318 * `'match'` *(string)*: use the minmatch to match the specified keys.
319 * Note: It will affect the range[gt/gte or lt/lte(reverse)] options maybe.
320 * `'limit'` *(number, default: `-1`)*: limit the number of results collected by this stream. This number represents a *maximum* number of results and may not be reached if you get to the end of the data first. A value of `-1` means there is no limit. When `reverse=true` the highest keys will be returned instead of the lowest keys.
321
322__return__
323
324* object: the read stream object
325
326
327#### Events
328
329the standard `'data'`, '`error'`, `'end'` and `'close'` events are emitted.
330the `'last'` event will be emitted when the last data arrived, the argument is the last raw key.
331if no more data the last key is `undefined`.
332
333
334## Extensible API
335
336Remember that each of these methods, if you implement them, will receive exactly the number and order of arguments described. Optional arguments will be converted to sensible defaults.
337
338### AbstractLevelDOWN(location)
339
340## Sync Methods
341
342### AbstractLevelDOWN#_openSync(options)
343### AbstractLevelDOWN#_getSync(key, options)
344### AbstractLevelDOWN#_isExistsSync(key, options)
345### AbstractLevelDOWN#_putSync(key, value, options)
346### AbstractLevelDOWN#_delSync(key, options)
347### AbstractLevelDOWN#_batchSync(array, options)
348
349## Async Methods
350
351### AbstractLevelDOWN#_open(options, callback)
352### AbstractLevelDOWN#_close(callback)
353### AbstractLevelDOWN#_get(key, options, callback)
354### AbstractLevelDOWN#_isExists(key, options, callback)
355### AbstractLevelDOWN#_put(key, value, options, callback)
356### AbstractLevelDOWN#_del(key, options, callback)
357### AbstractLevelDOWN#_batch(array, options, callback)
358
359If `batch()` is called without argument or with only an options object then it should return a `Batch` object with chainable methods. Otherwise it will invoke a classic batch operation.
360
361the batch should be rename to transact more accurate.
362
363<code>batch()</code> can be used for very fast bulk-write operations (both *put* and *delete*). The `array` argument should contain a list of operations to be executed sequentially, although as a whole they are performed as an atomic operation inside LevelDB. Each operation is contained in an object having the following properties: `type`, `key`, `value`, where the *type* is either `'put'` or `'del'`. In the case of `'del'` the `'value'` property is ignored. Any entries with a `'key'` of `null` or `undefined` will cause an error to be returned on the `callback` and any `'type': 'put'` entry with a `'value'` of `null` or `undefined` will return an error.
364
365```js
366var ops = [
367 { type: 'del', key: 'father' }
368 , { type: 'put', key: 'name', value: 'Yuri Irsenovich Kim' }
369 , { type: 'put', key: 'dob', value: '16 February 1941' }
370 , { type: 'put', key: 'spouse', value: 'Kim Young-sook' }
371 , { type: 'put', key: 'occupation', value: 'Clown' }
372]
373
374db.batch(ops, function (err) {
375 if (err) return console.log('Ooops!', err)
376 console.log('Great success dear leader!')
377})
378```
379
380### AbstractLevelDOWN#_chainedBatch()
381
382By default an `batch()` operation without argument returns a blank `AbstractChainedBatch` object. The prototype is available on the main exports for you to extend. If you want to implement chainable batch operations then you should extend the `AbstractChaindBatch` and return your object in the `_chainedBatch()` method.
383
384### AbstractLevelDOWN#_approximateSize(start, end, callback)
385
386### AbstractLevelDOWN#IteratorClass
387
388You can override the `IteratorClass` to your Iterator.
389After override this, it is not necessary to implement the `"_iterator()"` method.
390
391### AbstractLevelDOWN#_iterator(options)
392
393By default an `iterator()` operation returns a blank `AbstractIterator` object. The prototype is available on the main exports for you to extend. If you want to implement iterator operations then you should extend the `AbstractIterator` and return your object in the `_iterator(options)` method.
394
395`AbstractIterator` implements the basic state management found in LevelDOWN. It keeps track of when a `next()` is in progress and when an `end()` has been called so it doesn't allow concurrent `next()` calls, it does it allow `end()` while a `next()` is in progress and it doesn't allow either `next()` or `end()` after `end()` has been called.
396
397__arguments__
398
399* options *(obeject)*: optional object with the following options:
400 * `'gt'` (greater than), `'gte'` (greater than or equal) define the lower bound of the range to be streamed. Only records where the key is greater than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the records streamed will be the same.
401 * `'lt'` (less than), `'lte'` (less than or equal) define the higher bound of the range to be streamed. Only key/value pairs where the key is less than (or equal to) this option will be included in the range. When `reverse=true` the order will be reversed, but the records streamed will be the same.
402 * `'reverse'` *(boolean, default: `false`)*: a boolean, set true and the stream output will be reversed. Beware that due to the way LevelDB works, a reverse seek will be slower than a forward seek.
403 * `'keys'` *(boolean, default: `true`)*: whether contain keys.
404 * `'values'` *(boolean, default: `true`)*: whether contain values.
405 * `'limit'` *(number, default: `-1`)*: limit the number of results collected by this stream. This number represents a *maximum* number of results and may not be reached if you get to the end of the data first. A value of `-1` means there is no limit. When `reverse=true` the highest keys will be returned instead of the lowest keys.
406 * `'fillCache'` *(boolean, default: `false`)*: wheather LevelDB's LRU-cache should be filled with data read.
407
408
409
410
411### AbstractIterator(db)
412
413Provided with the current instance of `AbstractLevelDOWN` by default.
414
415### Sync methods:
416
417#### AbstractIterator#_nextSync()
418
419__return__
420
421* if any result: return a two elements of array
422 * the first is the key, the first element could be null or undefined if options.keys is false
423 * the second is the value, the second element could be null or undefined if options.values is false
424* or return false, if no any data yet.
425
426
427#### AbstractIterator#_endSync()
428
429### Async methods:
430
431#### AbstractIterator#_next(callback)
432#### AbstractIterator#_end(callback)
433
434### AbstractChainedBatch
435
436Provided with the current instance of `AbstractLevelDOWN` by default.
437
438### AbstractChainedBatch#_put(key, value)
439### AbstractChainedBatch#_del(key)
440### AbstractChainedBatch#_clear()
441### AbstractChainedBatch#_write(options, callback)
442
443<a name="contributing"></a>
444Contributing
445------------
446
447Abstract LevelDOWN is an **OPEN Open Source Project**. This means that:
448
449> Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project.
450
451See the [CONTRIBUTING.md](https://github.com/rvagg/node-levelup/blob/master/CONTRIBUTING.md) file for more details.
452
453### Contributors
454
455Abstract LevelDOWN is only possible due to the excellent work of the following contributors:
456
457<table><tbody>
458<tr><th align="left">Riceball LEE</th><td><a href="https://github.com/snowyu">GitHub/snowyu</a></td><td>&nbsp;</td></tr>
459<tr><th align="left">Rod Vagg</th><td><a href="https://github.com/rvagg">GitHub/rvagg</a></td><td><a href="http://twitter.com/rvagg">Twitter/@rvagg</a></td></tr>
460<tr><th align="left">John Chesley</th><td><a href="https://github.com/chesles/">GitHub/chesles</a></td><td><a href="http://twitter.com/chesles">Twitter/@chesles</a></td></tr>
461<tr><th align="left">Jake Verbaten</th><td><a href="https://github.com/raynos">GitHub/raynos</a></td><td><a href="http://twitter.com/raynos2">Twitter/@raynos2</a></td></tr>
462<tr><th align="left">Dominic Tarr</th><td><a href="https://github.com/dominictarr">GitHub/dominictarr</a></td><td><a href="http://twitter.com/dominictarr">Twitter/@dominictarr</a></td></tr>
463<tr><th align="left">Max Ogden</th><td><a href="https://github.com/maxogden">GitHub/maxogden</a></td><td><a href="http://twitter.com/maxogden">Twitter/@maxogden</a></td></tr>
464<tr><th align="left">Lars-Magnus Skog</th><td><a href="https://github.com/ralphtheninja">GitHub/ralphtheninja</a></td><td><a href="http://twitter.com/ralphtheninja">Twitter/@ralphtheninja</a></td></tr>
465<tr><th align="left">David Björklund</th><td><a href="https://github.com/kesla">GitHub/kesla</a></td><td><a href="http://twitter.com/david_bjorklund">Twitter/@david_bjorklund</a></td></tr>
466<tr><th align="left">Julian Gruber</th><td><a href="https://github.com/juliangruber">GitHub/juliangruber</a></td><td><a href="http://twitter.com/juliangruber">Twitter/@juliangruber</a></td></tr>
467<tr><th align="left">Paolo Fragomeni</th><td><a href="https://github.com/hij1nx">GitHub/hij1nx</a></td><td><a href="http://twitter.com/hij1nx">Twitter/@hij1nx</a></td></tr>
468<tr><th align="left">Anton Whalley</th><td><a href="https://github.com/No9">GitHub/No9</a></td><td><a href="https://twitter.com/antonwhalley">Twitter/@antonwhalley</a></td></tr>
469<tr><th align="left">Matteo Collina</th><td><a href="https://github.com/mcollina">GitHub/mcollina</a></td><td><a href="https://twitter.com/matteocollina">Twitter/@matteocollina</a></td></tr>
470<tr><th align="left">Pedro Teixeira</th><td><a href="https://github.com/pgte">GitHub/pgte</a></td><td><a href="https://twitter.com/pgte">Twitter/@pgte</a></td></tr>
471<tr><th align="left">James Halliday</th><td><a href="https://github.com/substack">GitHub/substack</a></td><td><a href="https://twitter.com/substack">Twitter/@substack</a></td></tr>
472<tr><th align="left">Thomas Watson Steen</th><td><a href="https://github.com/watson">GitHub/watson</a></td><td><a href="https://twitter.com/wa7son">Twitter/@wa7son</a></td></tr>
473</tbody></table>
474
475<a name="license"></a>
476License &amp; copyright
477-------------------
478
479Copyright (c) 2012-2014 Abstract LevelDOWN contributors (listed above).
480
481Abstract LevelDOWN is licensed under the MIT license. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.