Skip to content

iwillwen/min-model

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

min-model

This is a model layer for MinDB. It can make MinDB more human-friendly and more fit to the real application development.

Installation

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.

Polyfill

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'

Usage

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])

Indexes

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)

Indexes Usage

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))

Custom Indexer

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)}&param1=0.5&param2=0`)
        .then(res => res.text())
        .then(body => resolve(body.split('\r\n').filter(Boolean)))
        .catch(reject)
    })
  }
}

Contact.setIndexerForColumn('name', ChineseStringIndexer)

Build min-model

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

Test cases are coming soon.

About

A model layer for MinDB

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published