UNPKG

5.07 kBJavaScriptView Raw
1var Emitter = require('component-emitter')
2var inherits = require('inherits')
3var filter = require('filter-object-stream')
4var filterObject = require('filter-object')
5var validator = require('is-my-json-valid')
6var indexer = require('level-simple-indexes')
7var defaults = require('json-schema-defaults')
8var sublevel = require('subleveldown')
9var through = require('through2')
10var extend = require('extend')
11var cuid = require('cuid')
12
13module.exports = LevelModel
14inherits(LevelModel, Emitter)
15
16function LevelModel (db, opts) {
17 if (!(this instanceof LevelModel)) return new LevelModel(db, opts)
18 Emitter.call(this)
19 var self = this
20
21 this.schema = filterObject(opts, ['*', '!modelName', '!timestamp', '!indexKeys', '!validateOpts', '!prefix'])
22 this.modelName = opts.modelName
23 this.db = sublevel(db, this.modelName, { valueEncoding: 'json' })
24 this.timestamps = opts.timestamps === undefined ? true : opts.timestamps
25 this.timestamp = opts.timestamp || function () { return new Date(Date.now()).toISOString() }
26 this.indexKeys = opts.indexKeys || []
27
28 this.schema = extend({
29 title: self.modelName,
30 type: 'object'
31 }, this.schema)
32
33 this.schema.properties.key = {
34 type: 'string'
35 }
36
37 if (this.timestamps) {
38 this.schema.properties.created = {
39 type: ['string', 'null'],
40 default: null
41 }
42 this.schema.properties.updated = {
43 type: ['string', 'null'],
44 default: null
45 }
46 }
47
48 this.schema.required = this.schema.required || []
49 if (this.schema.required.indexOf('key') < 0) {
50 this.schema.required = this.schema.required.concat('key')
51 }
52
53 this.validateOpts = opts.validateOpts
54 this.validate = validator(this.schema, opts.validateOpts)
55
56 function map (key, callback) {
57 self.get(key, function (err, val) {
58 callback(err, val)
59 })
60 }
61
62 var indexOpts = opts.indexOpts || {
63 properties: this.indexKeys,
64 keys: true,
65 values: true,
66 map: map
67 }
68
69 this.indexDB = sublevel(db, this.modelName + '-index')
70 this.indexer = indexer(this.indexDB, indexOpts)
71}
72
73LevelModel.prototype.create = function (data, callback) {
74 var self = this
75 var key = data.key ? data.key : cuid()
76 if (!data.key) data.key = key
77 data = this.beforeCreate(data)
78 data = extend(defaults(this.schema), data)
79 var validated = this.validate(data)
80 if (!validated) return callback(new Error(JSON.stringify(this.validate.errors)))
81
82 if (this.timestamps) {
83 data.created = this.timestamp()
84 data.updated = null
85 }
86 this.db.put(key, data, function (err) {
87 if (err) return callback(err)
88
89 self.indexer.addIndexes(data, function () {
90 self.emit('create', data)
91 callback(null, data)
92 })
93 })
94}
95
96LevelModel.prototype.get = function (key, options, callback) {
97 this.db.get(key, options, callback)
98}
99
100LevelModel.prototype.put =
101LevelModel.prototype.save = function (key, data, callback) {
102 if (typeof key === 'object') {
103 callback = data
104 data = key
105 key = data.key
106 }
107
108 if (!key) return this.create(data, callback)
109 return this.update(key, data, callback)
110}
111
112LevelModel.prototype.update = function (key, data, callback) {
113 var self = this
114
115 if (typeof key === 'object') {
116 callback = data
117 data = key
118 key = data.key
119 }
120
121 this.get(key, function (err, model) {
122 if (err || !model) return callback(new Error(self.modelName + ' not found with key ' + key))
123 model = extend(model, data)
124 if (self.timestamps) model.updated = self.timestamp()
125 model = self.beforeUpdate(model)
126
127 var validated = self.validate(data)
128 if (!validated) return callback(new Error(JSON.stringify(self.validate.errors)))
129
130 self.indexer.updateIndexes(model, function () {
131 self.db.put(key, model, function (err) {
132 self.emit('update', model)
133 callback(err, model)
134 })
135 })
136 })
137}
138
139LevelModel.prototype.del =
140LevelModel.prototype.delete = function (key, callback) {
141 var self = this
142 this.get(key, function (err, data) {
143 if (err || !data) return callback(err)
144 self.indexer.removeIndexes(data, function () {
145 self.emit('delete', data)
146 self.db.del(key, callback)
147 })
148 })
149}
150
151LevelModel.prototype.createReadStream = function (options) {
152 return this.db.createReadStream(options)
153}
154
155LevelModel.prototype.find =
156LevelModel.prototype.createFindStream = function (index, options) {
157 return this.indexer.find(index, options)
158}
159
160LevelModel.prototype.findOne = function (index, options, callback) {
161 this.indexer.findOne(index, options, function (err, model) {
162 if (err) return callback(err)
163 if (!model) return callback(new Error('[NotFoundError: model not found with ' + index + ' ' + options + ']'))
164 return callback(null, model)
165 })
166}
167
168LevelModel.prototype.filter =
169LevelModel.prototype.createFilterStream = function (options) {
170 if (!options.query) var options = { query: options }
171 return this.createReadStream(options).pipe(through.obj(filter(options.query)))
172}
173
174LevelModel.prototype.beforeCreate = function (data) {
175 return data
176}
177
178LevelModel.prototype.beforeUpdate = function (data) {
179 return data
180}