UNPKG

21.3 kBMarkdownView Raw
1# Abstract NoSQL Database [![Build Status](https://secure.travis-ci.org/snowyu/node-abstract-nosql.png?branch=master)](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. And it make abstract-nosql modularization become possible.
8
9[abstract-nosql](https://github.com/snowyu/node-abstract-nosql) database can be extended its capabilities by adding different feature
10addin. and you(database developer) almost have nothing to do, can have these features. The database user can be free to decide whether to add this feature.
11
12* [nosql-stream](https://github.com/snowyu/node-nosql-stream): streamable ability. you need implement the [AbstractIterator](https://github.com/snowyu/node-abstract-iterator).
13* [nosql-encoding](https://github.com/snowyu/node-nosql-encoding): key/value encoding ability.
14
15abstract-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.
16
17just 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.
18
19Synchronous 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.
20ES6 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.
21
22The setImmediate package could be extended to use different implementation(setImmediate, nextTick, ES6 generator, node-fiber) in different environment.
23So the simulated asynchronous uses this way, if you do not implement the asynchronous methods.
24
25
26## Changes(diference from abstract-leveldown)
27
28### v2.x.x
29
30* the modularization(feature plugin) with abstract-nosql
31 * [nosql-encoding](https://github.com/snowyu/node-nosql-encoding)
32 * [nosql-stream](https://github.com/snowyu/node-nosql-stream)
33* Let the user decide whether to use these features.
34* (`broken changes`) remove the streamable feature from buildin. this is a plugin now.
35* (`broken changes`) defaults to disable asBuffer option.
36 * pls use the `getBuffer` method to get as buffer.
37
38
39### V1.x.x
40
41+ Can add the encoding key/value ability via two ways:
42 * see the [nosql-encoding](https://github.com/snowyu/node-nosql-encoding) package.
43 * see the [encoding-iterator](https://github.com/snowyu/node-encoding-iterator) package.
44+ getBuffer/getBufferSync(key, destBuffer, options) optional method.
45 * the key's value will be put into the destBuffer if destBuffer is not null.
46 * the options.offset added, write to the destBuffer at offset position. offset defaults to 0.
47 * the value will be truncated if the destBuffer.length is less than value's.
48 * return the byte size of value.
49 * the will use the get/getSync to simulate if no \_getBuffer implemented.
50- Remove the AbstractIterator to [abstract-iterator](https://github.com/snowyu/node-abstract-iterator) package
51+ Add the stream ability
52 * You should install [nosql-stream](https://github.com/snowyu/node-nosql-stream) package first to use this feature.
53+ Add the AbstractError and error code supports.
54* DB constructor allows no location.
55* Add IteratorClass supports.
56+ Add synchronous methods supports.
57 * Add the synchronous methods support now. You can implement the synchronous methods only.
58 * The asynchronous methods will be simulated via these synchronous methods. If you wanna
59 * support the asynchronous methods only, just do not implement these synchronous methods.
60 * But if you wanna support the synchronous only, you should override the asynchronous methods to disable it.
61+ Add isExists/isExistsSync optional method to test key whether exists.
62 * it will use the \_get/\_getSync method if no \_isExists or \_isExistsSync implemented
63 * iExist/iExistSync is the alias of iExists/iExistsSync.
64+ the AbstractNoSQL class supports events now.
65 * emit `'open'` and `'ready'` event after the database is opened.
66 * emit `'closed'` event after the database is closed.
67+ Add isOpen()/opened to test the database whether opened.
68+ Add mGetSync()/mGet() multi get keys method for the range(Array) option of the Iterator
69 * it will use the \_get/\_getSync method if no \_mGet or \_mGetSync implemented.
70 * Note: mGet/mGetSync return the array of object: [{key:key,value:value}, ...]
71 * But the \_mGet/\_mGetSync return the plain array: [key1, value1, key2, value2, ...]
72 + keys *(bool, default true)* option to return keys or not
73 * return the values array if keys is false
74 + raiseError *(bool, default true)* option to raise or ignore error
75 * some elements will be undefined for the value error if keys is false
76+ Add Iterator.nextSync
77 * note: nextSync return the object: {key:key, value:value}, return false if ending.
78 * But the \_nextSync return the array: [key, value]
79
80## AbstractError Classes
81
82see [abstract-object](https://github.com/snowyu/abstract-object)
83
84### AbstractError
85
86All Errors are derived from the AbstractError.
87
88* Members:
89 * message: the error message.
90 * code: the error code.
91* Methods:
92 * ok()
93 * notFound()
94 * ....
95 * invalidFormat()
96* Class Methods:
97 * AbstractError.isOk(err)
98 * AbstractError.isNotFound(err)
99 * ...
100
101the error codes:
102
103* AbstractError.Ok = 0
104* AbstractError.NotFound = 1
105* AbstractError.Corruption = 2
106* AbstractError.NotSupported = 3
107* AbstractError.InvalidArgument = 4
108* AbstractError.IO = 5
109* AbstractError.NotOpened = 6
110* AbstractError.InvalidType = 7
111* AbstractError.InvalidFormat = 8
112
113
114### Other Error Classes:
115
116* NotFoundError
117* CorruptionError
118* NotSupportedError/NotImplementedError
119* InvalidArgumentError
120* IOError
121* NotOpenedError
122* InvalidTypeError
123* InvalidFormatError
124* OpenError
125* CloseError
126* AlreadyEndError
127
128
129```js
130var OpenError = createError("CanNotOpen", NotOpened)
131var CloseError = createError("CanNotClose", 52)
132var AlreadyEndError = createError("AlreadyEnd", 53)
133```
134
135
136
137## Streamable plugin
138
139Once implements the [AbstractIterator](https://github.com/snowyu/node-abstract-iterator):
140
141* AbstractIterator.\_nextSync() or AbstractIterator.\_next().
142* AbstractIterator.\_endSync() or AbstractIterator.\_end().
143
144the db should be the streamable.
145
146But, you should install the [nosql-stream](https://github.com/snowyu/node-nosql-stream) package first.
147
148 npm install nosql-stream
149
150see [nosql-stream](https://snowyu/github.com/node-nosql-stream) for more details
151
152## Extensible API
153
154Remember 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.
155
156### AbstractNoSql(location)
157
158## Sync Methods
159
160### AbstractNoSql#_isExistsSync(key, options)
161
162this is an optional method for performance.
163
164### AbstractNoSql#_mGetSync(keys, options)
165
166this is an optional method for performance.
167
168__arguments__
169
170* keys *(array)*: the keys array to get.
171* options *(object)*: the options for get.
172
173__return__
174
175* array: [key1, value1, key2, value2, ...]
176
177### AbstractNoSql#_openSync(options)
178### AbstractNoSql#_getSync(key, options)
179### AbstractNoSql#_putSync(key, value, options)
180### AbstractNoSql#_delSync(key, options)
181### AbstractNoSql#_batchSync(array, options)
182
183
184## Async Methods
185
186### AbstractNoSql#_isExists(key, options, callback)
187
188this is an optional method for performance.
189
190### AbstractNoSql#_mGet(keys, options, callback)
191
192this is an optional method for performance.
193
194__arguments__
195
196* keys *(array)*: the keys array to get.
197* options *(object)*: the options for get.
198* callback *(function)*: the callback function
199 * function(err, items)
200 * items: [key1, value1, key2, value2, ...]
201
202### AbstractNoSql#_open(options, callback)
203### AbstractNoSql#_close(callback)
204### AbstractNoSql#_get(key, options, callback)
205### AbstractNoSql#_put(key, value, options, callback)
206### AbstractNoSql#_del(key, options, callback)
207### AbstractNoSql#_batch(array, options, callback)
208
209If `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.
210
211the batch should be rename to transact more accurate.
212
213<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.
214
215```js
216var ops = [
217 { type: 'del', key: 'father' }
218 , { type: 'put', key: 'name', value: 'Yuri Irsenovich Kim' }
219 , { type: 'put', key: 'dob', value: '16 February 1941' }
220 , { type: 'put', key: 'spouse', value: 'Kim Young-sook' }
221 , { type: 'put', key: 'occupation', value: 'Clown' }
222]
223
224db.batch(ops, function (err) {
225 if (err) return console.log('Ooops!', err)
226 console.log('Great success dear leader!')
227})
228```
229
230### AbstractNoSql#_chainedBatch()
231
232By 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.
233
234### AbstractNoSql#_approximateSize(start, end, callback)
235
236### AbstractNoSql#IteratorClass
237
238You can override the `IteratorClass` to your Iterator.
239After override this, it is not necessary to implement the `"_iterator()"` method.
240
241### AbstractNoSql#_iterator(options)
242
243By 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.
244
245`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.
246
247__arguments__
248
249* options *(obeject)*: optional object with the following options:
250 * `'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.
251 * `'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.
252 * `'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.
253 * `'keys'` *(boolean, default: `true`)*: whether contain keys.
254 * `'values'` *(boolean, default: `true`)*: whether contain values.
255 * `'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.
256 * `'fillCache'` *(boolean, default: `false`)*: wheather LevelDB's LRU-cache should be filled with data read.
257
258
259### AbstractChainedBatch
260
261Provided with the current instance of `AbstractNoSql` by default.
262
263### AbstractChainedBatch#_put(key, value)
264### AbstractChainedBatch#_del(key)
265### AbstractChainedBatch#_clear()
266### AbstractChainedBatch#_write(options, callback)
267
268## Example
269
270A simplistic in-memory LevelDOWN replacement
271
272use sync methods:
273
274
275```js
276var util = require('util')
277 , AbstractNoSql = require('./').AbstractNoSql
278
279// constructor, passes through the 'location' argument to the AbstractNoSql constructor
280function FakeNoSqlDatabase (location) {
281 AbstractNoSql.call(this, location)
282}
283
284// our new prototype inherits from AbstractNoSql
285util.inherits(FakeNoSqlDatabase, AbstractNoSql)
286
287// implement some methods
288
289FakeNoSqlDatabase.prototype._openSync = function (options) {
290 this._store = {}
291 return true
292}
293
294FakeNoSqlDatabase.prototype._putSync = function (key, value, options) {
295 key = '_' + key // safety, to avoid key='__proto__'-type skullduggery
296 this._store[key] = value
297 return true
298}
299
300//the isExists is an optional method:
301FakeNoSqlDatabase.prototype._isExistsSync = function (key, options) {
302 return this._store.hasOwnProperty('_' + key)
303}
304
305FakeNoSqlDatabase.prototype._getSync = function (key, options) {
306 var value = this._store['_' + key]
307 if (value === undefined) {
308 // 'NotFound' error, consistent with LevelDOWN API
309 throw new Error('NotFound')
310 }
311 return value
312}
313
314FakeNoSqlDatabase.prototype._delSync = function (key, options) {
315 delete this._store['_' + key]
316 return true
317}
318
319//use it directly
320
321var db = new FakeNoSqlDatabase()
322
323//sync:
324db.put('foo', 'bar')
325var result = db.get('foo')
326
327//async:
328db.put('foo', 'bar', function (err) {
329 if (err) throw err
330 db.get('foo', function (err, value) {
331 if (err) throw err
332 console.log('Got foo =', value)
333 db.isExists('foo', function(err, isExists){
334 if (err) throw err
335 console.log('isExists foo =', isExists)
336 })
337 })
338})
339
340//stream:
341
342db.readStream().on('data', function(data){
343})
344
345// Or use it in LevelUP
346
347var levelup = require('levelup')
348
349var db = levelup('/who/cares/', {
350 // the 'db' option replaces LevelDOWN
351 db: function (location) { return new FakeNoSqlDatabase(location) }
352})
353
354//async:
355db.put('foo', 'bar', function (err) {
356 if (err) throw err
357 db.get('foo', function (err, value) {
358 if (err) throw err
359 console.log('Got foo =', value)
360 db.isExists('foo', function(err, isExists){
361 if (err) throw err
362 console.log('isExists foo =', isExists)
363 })
364 })
365})
366
367//sync:
368db.put('foo', 'bar')
369console.log(db.get('foo'))
370console.log(db.isExists('foo'))
371```
372
373use async methods(no sync supports):
374
375
376```js
377var util = require('util')
378 , AbstractNoSql = require('./').AbstractNoSql
379
380// constructor, passes through the 'location' argument to the AbstractNoSql constructor
381function FakeNoSqlDatabase (location) {
382 AbstractNoSql.call(this, location)
383}
384
385// our new prototype inherits from AbstractNoSql
386util.inherits(FakeNoSqlDatabase, AbstractNoSql)
387
388// implement some methods
389
390FakeNoSqlDatabase.prototype._open = function (options, callback) {
391 // initialise a memory storage object
392 this._store = {}
393 // optional use of nextTick to be a nice async citizen
394 process.nextTick(function () { callback(null, this) }.bind(this))
395}
396
397FakeNoSqlDatabase.prototype._put = function (key, value, options, callback) {
398 key = '_' + key // safety, to avoid key='__proto__'-type skullduggery
399 this._store[key] = value
400 process.nextTick(callback)
401}
402
403//the isExists is an optional method:
404FakeNoSqlDatabase.prototype._isExists = function (key, options, callback) {
405 var value = this._store.hasOwnProperty('_' + key)
406 process.nextTick(function () {
407 callback(null, value)
408 })
409}
410
411FakeNoSqlDatabase.prototype._get = function (key, options, callback) {
412 var value = this._store['_' + key]
413 if (value === undefined) {
414 // 'NotFound' error, consistent with LevelDOWN API
415 return process.nextTick(function () { callback(new Error('NotFound')) })
416 }
417 process.nextTick(function () {
418 callback(null, value)
419 })
420}
421
422FakeNoSqlDatabase.prototype._del = function (key, options, callback) {
423 delete this._store['_' + key]
424 process.nextTick(callback)
425}
426
427// now use it in LevelUP
428
429var levelup = require('levelup')
430
431var db = levelup('/who/cares/', {
432 // the 'db' option replaces LevelDOWN
433 db: function (location) { return new FakeNoSqlDatabase(location) }
434})
435
436db.put('foo', 'bar', function (err) {
437 if (err) throw err
438 db.get('foo', function (err, value) {
439 if (err) throw err
440 console.log('Got foo =', value)
441 })
442})
443```
444
445See [nosql-memdb](https://github.com/snowyu/node-nosql-memdb/) if you are looking for a complete in-memory replacement for AbstractNoSql database.
446
447
448<a name="contributing"></a>
449Contributing
450------------
451
452Abstract LevelDOWN is an **OPEN Open Source Project**. This means that:
453
454> 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.
455
456See the [CONTRIBUTING.md](https://github.com/rvagg/node-levelup/blob/master/CONTRIBUTING.md) file for more details.
457
458### Contributors
459
460Abstract LevelDOWN is only possible due to the excellent work of the following contributors:
461
462<table><tbody>
463<tr><th align="left">Riceball LEE</th><td><a href="https://github.com/snowyu">GitHub/snowyu</a></td><td>&nbsp;</td></tr>
464<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>
465<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>
466<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>
467<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>
468<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>
469<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>
470<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>
471<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>
472<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>
473<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>
474<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>
475<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>
476<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>
477<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>
478</tbody></table>
479
480<a name="license"></a>
481License &amp; copyright
482-------------------
483
484Copyright (c) 2012-2014 Abstract LevelDOWN contributors (listed above).
485
486Abstract 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.