verymodel-riak
Riak extensions for VeryModel
- Author: Aaron McCall aaron@andyet.net
- Version: 0.11.3
- License: MIT
Examples
Using only default functionality
var VeryRiakModel = VeryRiakModel;
Define our fields
var MyDef = first_name: {} last_name: index: true name: private: true { return thisfirst_name + ' ' + thislast_name; } age: index: integer: true gender: index: true private: true city: {} state: index: true zip: index: integer: true model: default: 'person' required: true private: true static: true favorite_foods: index: isArray: true ;
Define our indexes
var MyOptions = allKey: 'model' bucket: "test:bucket";
Init our model factory
var MyModel = MyDef MyOptions;
Create a model instance
var myInstance = MyModel; myInstance; // stores the instance's data to Riak
myInstance.indexes will return:
key: 'last_name_bin' value: 'Jones' key: 'age_int' value: 40 key: 'gender_bin' value: 40 key: 'state_bin' value: 'GA' key: 'zip_int' value: 30303 key: 'model_bin' value: 'person' key: 'favorite_foods_bin' value: 'pizza' key: 'favorite_foods_bin' value: 'fried chicken' key: 'favorite_foods_bin' value: 'applesauce' key: 'favorite_foods_bin' value: 'cake'
myInstance.value will return:
first_name: data0first_name last_name: 'Jones' city: 'Atlanta' state: 'GA' zip: 30303
Defaults
var _ = ;var indexes = ;var request = ;var streams = ; moduleexports =
definition
definition:
id: The "id" field is where we'll store the Riak key. By default it will just be an unvalidated, public field
id: {}
indexes: We'll need a way to retrieve all of the fields that should be indexed, so by default that will be all of the fields defined as indexes
indexes: private: true derive: indexesderive
value: By default, all non-private fields that aren't the id are expected to be the value payload
value: private: true { var model = this__verymetamodel; if !modeloptionsvalues modeloptionsvalues = _compact_
By default we'll use all of the public fields except id
return defprivate || isKeyField || isAllKey ? false : key; })); return _; } } }
model methods
methods:
getAllKey: Allows us to share a bucket between different model types
{ if thisoptionsallKey === '$bucket' return key: thisoptionsallKey def: default: this; var allKeyDef = thisoptionsallKey && thisdefinitionthisoptionsallKey
default + required ensures that the allKey is always populated private ensures it's not stored as part of the object's data static ensures that the default value is not overwritten
allKeyIsValid = allKeyDef && allKeyDefdefault && allKeyDefrequired && allKeyDefstatic; if allKeyDef && allKeyIsValid return key: thisoptionsallKey + '_bin' def: allKeyDef; }
getBucket: Returns default bucket optionally appending an additional namespace
{ var bucket = thisoptionsbucket; if !bucket && thisoptionsriak bucket = thisoptionsriakbucket; if bucket && !append return bucket; if bucket && append return bucket append; throw 'Please set a Riak bucket via options.bucket'; }
getLogger: Returns specified logger if defined or creates one and returns it
{ return thisoptionslogger || thisoptionslogger = ; }
getRequest: Builds Riak request objects. Signature varies according to type and developer preference. Supported request types are: del, get, index, mapreduce, and search.
- Build request to get a single object by its key:
- type and key:
model.getRequest('get', 'my-riak-key')
or - type and object:
model.getRequest('get', {key: 'my-riak-key'})
- type and key:
- Build request to get a list of keys for an index exact match:
- type, index, key:
model.getRequest('index', 'my_index_bin', 'foo')
or - type and object:
model.getRequest('index', {index: 'my_index_bin', key: 'foo'})
- type, index, key:
- Build request to get a list of keys via an index range search:
- type, index, min, max:
model.getRequest('index', 'my_index_bin', 'bar', 'foo')
or - type and object:
model.getRequest('index', {index: 'my_index_bin', range_min: 'bar', range_max: 'foo'})
- type, index, min, max:
- Build request for mapreduce:
- type, index, key, query array:
model.getRequest('mapreduce', 'my_index', 'my_key', […map/reduce phases…])
or - type and object:
model.getRequest('mapreduce', {inputs: …my inputs…, query: […map/reduce phases…]})
or - type, inputs array, query array:
model.getRequest('mapreduce', […my inputs…], […map/reduce phases…])
- type, index, key, query array:
- Build request to search:
- type, index, q:
model.getRequest('search', 'my_index', 'name:Bill')
or - type and object:
model.getRequest('search', {index: 'my_index', q: 'name:Bill'})
- type, index, q:
- Finally, any type of request can be created according to the following format:
model.getRequest({ type: 'index', options: {index: 'my_index_bin', key: 'foo'}})
where type is any one of the types listed and options
{ if _ && typetype && typeoptions return requesttypetypethis typeoptions; return requesttypethis _; }
_indexQuery: Index query wrapper that returns a stream
{ var args = _; args; var request = thisgetRequest;
Return the readable stream
return this; }
all: Streams or calls back with all instances of this model that are stored in Riak or an index-filtered set of them, depending on whether filtering args are passed. If the first argument is a function, it will be called with the result.
{ var args = _ streaming = typeof args0 !== 'function' cb = !streaming ? args0 : null requestArgs = _ bucket; if argslength > 1 && typeof args1 === 'object' bucket = args1bucket; var logger = this; logger;
All stream handling is done via a Transform stream that receives our key stream and transmits instances
var stream = this_indexQuery streamOpts = model: this bucket: bucket; return stream ; }
find: Simplifies index lookups and can be called with the values or options object signatures.
- values: find('index', 'key_or_min', ['max',] [function (err, instances) {}])
- options object (shown with range query—-substitute key for range_min/range_max for exact match): find({index: 'index', range_min: 'min', range_max: 'max'}, [function (err, instance)])
{ var args = _ hasCb = typeof _ === 'function' cb = hasCb && args; if typeof index === 'string' && !index index = indexes; args; if cb args; return thisall; }
find: searches for matching indexesToData: Reformats indexes from Riak so that they can be applied to model instances
indexesToData: indexesrehydrate
replyToData: Reformats riak reply into the appropriate format to feed into an instance's loadData method
{ this; if !reply || !replycontent return {}; var content = replycontentlength > 1 ? thisoptions : replycontent0;
reformat our data for VeryModel
var indexes = {}; indexes = this; var data = _; if replykey datathisoptionskeyField = replykey; if replyvclock datavclock = replyvclock; return data; } { var reqArgs = 'get' id; if typeof bucket === 'function' && typeof cb === 'undefined' cb = bucket; bucket = undefined; if bucket reqArgs; this; } {
Resolve siblings, if necessary, or just grab our content
var data = this; datathisoptionskeyField = id; if bucket && thisdefinitionbucket databucket = bucket; var instance = this; if bucket && !instancebucket instancebucket = bucket; return instance; }
load: Load an object's data from Riak and creates a model instance from it.
{ var self = this; if typeof bucket === 'function' && typeof cb === 'undefined' cb = bucket; bucket = undefined; this
Override default toJSON method to make more Hapi compatible
if typeof cb === 'function' ; }); }
remove: Remove an instance from Riak
{ var self = this; if typeof bucket === 'function' && typeof cb === 'undefined' cb = bucket; bucket = undefined; this; this; }
backwards compatibility
remove: moduleexportsmethodsdelete }
options
options:
- Default allKey is Riak's magic 'give me all the keys' index
allKey: '$bucket'
- Default key field is id
keyField: 'id'
- pagination is on by default to prevent overloading the server
max_results: 100 paginate: true
- Default sibling handler is "last one wins"
{ return _; } }
instanceMethods
instanceMethods:
wrapper around this instance's
{}
prepare: Prepare a Riak request object from this instance.
{ var content = value: JSON content_type: 'application/json' ; var indexes = thisindexes; if indexeslength contentindexes = indexes; var payload = content: content bucket: this return_body: true ; if thisid payloadkey = thisid; if thisvclock payloadvclock = thisvclock; return payload; }
save: Put this instance to Riak. May be called with an options object, currently only for the purpose of passing { validate: false } to bypass validation
{ if !opts || optsvalidate !== false var errors = this; if errorslength return ; var self = this; var logger = this; var payload = this; logger; this
The boolean arg prevents a race condition when reply.content.length continues to be > 1
self; } if typeof cb === 'function' ; }); }
getClient: Proxy method to get the Riak client from model
{ return this__verymetamodel; }
getBucket: return instance-level bucket property or fall back to model's getBucket
{ return typeof thisbucket !== 'undefined' ? thisbucket : this__verymetamodel; }
getLogger: return this instance's logger (if defined) or fall back to model's getLogger
{ return typeof thislogger !== 'undefined' ? thislogger : this__verymetamodel; } }};
Add some logging
var { if typeof objname !== 'function' return; var method = objname; objname = _;};'load' 'remove' 'find' 'all';;;
Acknowledgements
- First and foremost, thanks to @fritzy for making VeryModel without which verymodel-riak would be non-existent or pointless.
- Contributors: