Master Quest SQLite3
Master Quest is a brave new attempt at Data Mapping.
It kinda looks like an ORM, but it isn't. It's not SQL, it's NoSQL with benefits.
You get to choose which contraints to keep and which to forget.
Guiding Principles
- NoSQL (well, as far as you care)
- Migrations don't suck
- Harness the awesomeness of indexes
-
deletedAt
to delete a record -
null
to delete a field - unindexed fields stored as json
- ids should be one either
- deterministic (duplicate records not okay, last
updatedAt
wins) - cryptographically random (duplicate records consolidated in application)
- deterministic (duplicate records not okay, last
- avoid mutating data (CRDT-ish style)
- if it won't scale, don't fret it (no foreign key contraints)
-
id
is simply namedid
- multi-key ids should be
sha256sum(key1 + key2 + key3)
- JavaScript is
camelCase
y, databases aresnake_case
y. We can handle that. - join tables are in alphabet order i.e.
foo
,bar
,bar_foo
TODO / In Progress
- Multi-Master Replication
- Relationships
- currently detaches before saving (most important)
- MongoDB / RethinkDB -ish queries
- RealTime
USAGE
npm install --save 'https://github.com/coolaj86/node-masterquest-sqlite3.git'
'use strict';
// works with sqlite3, sqlcipher, and sqlite3-cluster
var db = new (require('sqlite3').Database)('/tmp/data.sqlite3');
require('masterquest-sqlite3').wrap(db, {
modelname: 'Persons'
, indices: [ 'firstName', 'lastName' ]
, hasMany: [ 'children' ]
}).then(function (mq) {
// update (or create) deterministic record
var john = {
id: 'john.doe@email.com'
, firstName: 'john'
, lastName: 'doe'
, dog: { name: 'ralph', color: 'gold' }
, children: [ 'stacey@email.com' ]
};
mq.Persons.upsert(john.id, john).then(function () {
// note: if `dog` existed, it will be overwritten, not merged
// note: `children` will be removed before save
mq.Persons.get('john.doe@email.com').then(function (data) {
// dog will be rehydrated from json
// children will not be fetched and attached
console.log(data);
});
});
});
API
It's kinda CRUDdy... but don't let that scare you.
-
upsert(id, data[, oldId])
- creates or updates based on existence in DB (use this)- modifies
createdAt
and orupdatedAt
- modifies
-
create(id, obj)
- same as above, but fails if the object exists -
save(data[, oldId])
- (just don't use this, please) creates or updates based on presence of ID -
destroy(id)
- mark a record asdeletedAt
from DB -
get(id)
- grab one by id -
find(attrs, opts)
- grab many by indexable attributes- attrs
- explicit
null
will find all (and requires thatlimit
be set) -
{ foo: 2, bar: 6 }
will find records wherefoo
is2
andbar
is6
- explicit
- opts
orderBy
orderByDesc
limit
- attrs
Schema
Anything that isn't in the schema
-
indices
specifies an array of strings[ 'firstName', 'lastName' ]
- relationships are option and current only exclude during save
-
hasMany
,belongsTo
,hasOne
,belongsToMany
,hasAndBelongsToMany
-
-
createdAt
,updatedAt
,deletedAt
timestamps are always added- turn off with
timestamps: false
- turn off with
-
id
is alwaysid
- change with
idname: 'myId'
- change with
Migrations
You can only add indexes. You cannot rename or remove them.
To add an index, simply change the schema.
{ modelname: 'persons'
, indices: [ 'firstName', 'lastName' ]
, hasMany: [ 'children' ]
}
LICENSE
Dual-licensed MIT and Apache-2.0
See LICENSE