This is a model layer for MinDB. It can make MinDB more human-friendly and more fit to the real application development.
min-model
can be install by NPM.
$ npm install min-model --save --production
Of course, you can include it into your page with <script>
tag.
<script type="application/javascript" src="/path/to/script/model.js"></script>
BTW, min-model
is also friendly with browserify, webpack and rollup.
min-model
is using Symbol, Map and Promise that are new features in ES2015.
So unfortunately if you want to use min-model
in the old browsers, you are need to include the polyfills to make them work. Such as es6-shim
and es6-symbol
.
$ npm i es6-shim es6-symbol --save-dev
import 'es6-shim'
import 'es6-symbol/implement'
Obviously, min-model
is depends on MinDB. So you need to install MinDB first.
import min from 'min'
import Model from 'min-model'
// And then you must make `min-model` knows
// which MinDB database it should use.
// Bc MinDB has the `min.fork()` method to
// fork a new database.
Model.use(min)
Now, you can extend a model which you need to use. For example, here is a Contacts app, so we need to create a Contact
model to perform every person in the list.
// Syntax: Model.extend(name: String, columns: Object)
const Contact = Model.extend('contact', {
name: String,
memo: 'There is nothing about him/her.',
// `min-model` will automatically detect
// this column is a String column.
number: Number
})
Model.extend
receives an object that declares columns of the model. Is can include the native type constructor or the default value of a new one.
const contact = new Contact({
name: 'Will Wen Gunn',
number: 13800138000
})
Here are the constructor of the Model and its subclasses.
Key | Function | Return |
---|---|---|
key |
The unique key of the instance | String |
get(key) |
Fetch the value on the key |
Promise(Any) |
set(key, value) |
Modify the value on the key |
Promise([ String, Any ]) |
reset(key) |
Reset the value on the key back the default one |
Promise |
reset() |
Reset the whole instance | Promise |
remove() |
Remove the instance from the database | Promise |
The instances also have an event will be emitted.
Event | Trigger |
---|---|
ready |
When the instance data was stored in the database and be ready to process |
Lifecyle
The model instance have a lifecyle like following:
- beforeValidate
- beforeStore
- ready
- beforeUpdate
- afterUpdate
- beforeRemove
- afterRemove
These lifecycle hook can simply use in the Model.extend()
.
const Contact = Model.extend('contact', {
name: String,
memo: 'There is nothing about him/her.',
number: Number,
beforeValidata(content) {
// convert the number to integer
content.age = parseInt(content)
}
})
For real application development, the subclasses of Model also have some static methods for management and searching.
Key | Function | Return |
---|---|---|
fetch(key) |
Fetch the exists instance by its key | Promise(Model) |
setIndex(column) |
Set a indexer on the column for fast searching | Indexer |
search(column, query) |
Searching the instances by the column | Promise([Model]) |
For fast searching, min-model
provide a set of simple but strong algorithms for creating some indexes on the database.
By default, min-model
support the native types following.
- String (which can be split by comma(
,
), dot(.
), colon(:
), semicolon(;
), exclamation mark(!
), quotation mark("
and'
) and spaces) - Number
- Boolean
- Object
- Array
- Date
- Error (base on the messages of the errors)
For creating indexes, you need to call a static method of the subclasses.
// Syntax: Model.setIndex(column) : Indexer
let indexer = Contact.setIndex('name')
indexer = Contact.setIndex('number')
Indexer also has a ready
event will be emitted when it is ready for use, but it is not necessary for listening.
// Syntax: Model.search(column, query)
Contact
.search('name', 'Mike') // maybe there are not just one Mike
.search('memo', 'Engineer')
// Yes, you can search a column without creating a indexer
// `min-model` will use the last search result as the pre-flight data
// in this search operation.
.then(result => console.log(result))
If you think the default indexers are not fit for your application, you can build a custom indexer for it.
min-model
provides a BaseIndexer
which is bases on Inverted index algorithm. You can extends a subclass from it and overwrite the indexMapper
method and search
(not required) method easily to build your own indexer.
After that, you can use Model.setIndexer(type, Indexer)
to set up.
Method | Function | Return | Requred |
---|---|---|---|
indexMapper(value) |
The method to convert the value into the indexes | Array | √ |
search(query, preData) |
The layer method to receive the search result from the BaseIndexer and return it to the logical program |
Promise |
import moment from 'moment'
// Here is a 3rd module named moment.js
class FormatedDateIndexer extends Model.BaseIndexer {
indexMapper(value) {
// ...
// value would like '2016-05-01'
return moment(value).format('YYYY-MM-DD').split('-').map(Number)
}
}
Model.setIndexer(Date, FormatedDateIndexer)
search
method is a upper layer method for receive the result from the bottom layer and passing it back to the logical program. When indexes from indexMapper
could not filter the result correctly, you will need to overwrite the search
method for the last processing.
search
method must receive two arguments, the first one is the query condition and another is the previous data from the last searching operation. The result from the bottom layer can be received by calling the this._search
, you should call it and passing the same arguments.
export default class NumberIndexer extends Model.BaseIndexer {
indexMapper(number) {
return [
number % 3,
number % 5,
number % 7
]
}
search(query, preData) {
return this._search(query, preData)
.then(result => Promise.resolve(
result.filter(item => item[this.key] === query)
))
}
}
Of course, you can set the custom indexer just for a single column too.
Contact.setIndexerForColumn('number', NumberIndexer)
Async Indexer
In sometime, computing the indexes in the local device is not wise so we need to use some API to achieve.
You need to set a property named async
to be true
and indexMapper
method should returns a Promise object.
class ChineseStringIndexer extends Model.BaseIndexer {
get async() { return true }
indexMapper(val) {
return new Promise((resolve, reject) => {
fetch(`http://api.pullword.com/get.php?source=${encodeURIComponent(val)}¶m1=0.5¶m2=0`)
.then(res => res.text())
.then(body => resolve(body.split('\r\n').filter(Boolean)))
.catch(reject)
})
}
}
Contact.setIndexerForColumn('name', ChineseStringIndexer)
If you wanna build min-model
by yourself, you need to clone the project to your machine and install the development dependences.
$ cd min-model
$ npm install .
Make your change and run.
$ npm run-script build
Test cases are coming soon.