db.save('airlines', 'KLM', {fleet: 111, country: 'NL'}, { links:
[{ bucket: 'flights', key: 'KLM-8098', tag: 'cargo' },
{ bucket: 'flights', key: 'KLM-1196', tag: 'passenger' }]
})
db.getAll('flights', { where: { ready: true }})
Please note: Protobuf support is still experimental.
// npm install riak-js@latest
var db = require('riak-js').getClient()
// git clone git://github.com/frank06/riak-js.git # or cloning the repo
var db = require('/path/to/riak-js/lib').getClient()
// configure the host and port
var db = require('riak-js').getClient({host: "riak.myhost", port: "8098" });
Or specify the API: getClient({api: 'protobuf'})
.
db.get('flights', 'KLM-5034', function(err, flight, meta) {
if (err) throw err
flight.status = 'delayed'
meta.links.push({ bucket: 'airlines', key: 'IBE', tag: 'operated_by' })
db.save('flights', 'KLM-5034', flight, meta)
})
Meta is an important concept in riak-js. It is a implementation-agnostic object that carries all metadata associated to a document, such as the bucket, key, vclock, links, and so on. It is meant to be recycled — all properties that make sense to be updated for a subsequent store operation can be modified and sent back. Any given properties that aren't used by Riak are assumed to be custom metadata for Riak values. This will become more clear as we go through the guide.
An example meta
object could look like:
{ bucket: 'riakjs_airlines'
, key: 'CPA'
, usermeta: { important: false }
, _type: 'application/json'
, binary: false
, links:
[ { tag: 'flight'
, key: 'CPA-729'
, bucket: 'riakjs_client_test_flights'
}
]
, raw: 'riak'
, clientId: 'riak-js'
, host: 'localhost'
, vclock: 'a85hYGBgymDKBVIsTO+1QzKYEhnzWBm+rRc6xgcRZmtOYvg6tx4q8QMkkQUA'
, lastMod: 'Sat, 25 Sep 2010 17:40:08 GMT'
, etag: '8I9CsEwo8kScElgvCOC0k'
, statusCode: 200
}
Riak properties such as 'contentType', 'vclock', 'clientId', 'links', 'etag', 'r', 'w', 'dw', 'returnbody'
can all be set on this object. It also contains handy methods to deal with links, and provides sensible defaults, which can of course be overriden.
Examples are contentType: 'application/json'
and clientId: 'riak-js'
.
Not only these are tunable per-request. If you need certain defaults to apply to the whole session,
provide them at initialization time: getClient({clientId: 'lan-27', raw: 'data', debug: true})
.
fs.readFile("drunk-pilot.png", 'binary', function (err, image) {
if (err) throw err;
db.save('evidence', 'pilot-smith-drunk', image, { contentType: 'jpeg', immediateAction: 'fire' })
});
Note that 'jpeg'
is a shortcut and immediateAction
is custom metadata.
Buffer
s are only returned when the responseEncoding
property is set to binary
.
This happens automatically for known binary types, such as image/*
, video/*
or
application/octet-stream
, otherwise you have to provide it through Meta
.
An option (Meta
property) encodeUri
can be set to true
when you want to have your bucket and key URI-encoded. The default is false
because Meta
is implementation-agnostic, and this only makes sense for HTTP clients.
You can pass in a headers
options that will override any previously set header. Useful to set other headers if you have a reverse proxy sitting between node and Riak. Use with caution.
db.get('flights', 'KLM-5034', { headers: { Authorization: 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' } })
If you don't provide a content type while sending a request body, riak-js will do its best to guess one:
Buffer
it will assume application/octet-stream
typeof
yields object
, it will assume application/json
text/plain
db.save('flights', 'KLM-5034', flight)
riak-js follows the Node convention: last argument is the callback, whose first argument is the error object. Besides, the client will also emit the clientError
event.
If you don't provide a callback the result will always be logged through console.log
. You can choose to enable debugging via the { debug: true }
option.
All commands take two optional last arguments: meta
(options) and callback
,
in that order, and so they will not necessarily be shown below.
db.get('airlines', 'KLM')
A typical response would be:
{ name: 'KLM'
, fleet: 111
, alliance: 'SkyTeam'
, european: true
}
If, however, there is a sibling conflict (when allow_mult = true
) then a typical response would have a meta.statusCode = 300
and would look like:
[ { meta:
{ bucket: 'airlines'
, key: 'KLM'
, usermeta: {}
, _type: 'application/json'
, binary: false
, links: []
, raw: 'riak'
, clientId: 'riak-js'
, host: 'localhost'
, lastMod: 'Sun, 26 Sep 2010 16:28:17 GMT'
, etag: '5QDmB8ezT8hpMNX9Ias8DU'
, vclock: 'a85hYGBgymDKBVIsTO+1QzKYEhnzWBkWfhA+xgcRZmtOYlvXp4MskQUA'
}
, data: { name: 'KLM'
, fleet: 111
, alliance: 'SkyTeam'
, european: true
}
}
, { meta:
{ bucket: 'airlines'
, key: 'KLM'
, usermeta: {}
, _type: 'application/json'
, binary: false
, links: []
, raw: 'riak'
, clientId: 'riak-js'
, host: 'localhost'
, lastMod: 'Sun, 26 Sep 2010 16:28:17 GMT'
, etag: '4wz9tAlKC49RVqQmhcAvHz'
, vclock: 'a85hYGBgymDKBVIsTO+1QzKYEhnzWBkWfhA+xgcRZmtOYlvXp4MskQUA'
}
, data: { name: 'KLM'
, fleet: 113
, alliance: 'SkyTeam'
, european: true
}
}
]
Head will only get the meta
object back — no data. (It uses the HTTP HEAD verb under the hood.)
db.head('airlines', 'KLM')
Exists is a shortcut to tell you if a document exists or not. Internally, it uses head
and checks for a 404
.
db.exists('airlines', 'AIR_FRIGGIN_MADRID')
Just like as with the sibling conflict, getAll
will return an Array
of Object
s
with the meta
and data
properties.
db.getAll('airlines')
db.getAll('airlines', { where: { country: 'NL', fleet: 111 } })
db.buckets()
db.keys('airlines')
db.keys('airlines', { keys: 'stream' }).on('keys', console.dir).start()
Yes, in this case db.keys
will return an EventEmitter
. It will emit both the keys
and end
events.
db.count('airlines')
db.walk('airlines', 'KLM', [["_", "flight"]])
Provide { noJSON: true }
if you are not targeting JSON data!
db.save('airlines', 'ARG', { name: 'AerolĂneas Argentinas', fleet: 40, european: false })
db.save('flights', 'KLM-5034', flight, { returnbody: true, dw: 'quorum', method: 'POST' })
db.update('airlines', 'ARG', { fleet: 41 })
You may remove properties by setting them to undefined
:
db.update('airlines', 'ARG', { european: undefined })
db.remove('airlines', 'KLM')
Three variations of the same query:
db.add('albums').map('Riak.mapValuesJson').run()
db.add('albums').map({name: 'Riak.mapValuesJson', keep: true }).run()
db.add('albums').map(function(v) { return [Riak.mapValuesJson(v)[0]] }).run()
You can chain any number of phases or pass arrays, too:
db
.add('airlines')
.link({ bucket: 'flights', keep: false })
.map('Riak.mapValuesJson')
.reduce(['Riak.filterNotFound', function(value, count) { return value.slice(0, count - 1) }])
.run(function(err, flights) {
console.log(flights)
})
If you need to pass-in arguments, both map
and reduce
accept a second argument that will end up as the third of the map function:
db.add('flights').map(function (value, keyData, arg) { /* do something with arg == { cancelled: true } */ }, { cancelled: true })
You can also use key filters:
db.add({ bucket: 'flights', key_filters: [["matches", "KLM"]] }).map('Riak.mapValuesJson').run()
For input syntax follow the API. add
simply takes the value of the inputs
property.
These commands (getLarge
, saveLarge
, removeLarge
) behave much like
their counterparts get
, save
, remove
.
Except they don't take a bucket
argument, internally reference the luwak
raw resource, and always use responseEncoding = 'binary'
therefore returning Buffer
s.
db.getLarge('lowcost-pilot')
db.saveLarge('lowcost-pilot', buffer)
db.removeLarge('lowcost-pilot')
callback
db.ping()
callback
db.stats()
db.updateProps('airlines', { n_val: 8, allow_mult: true })
db.getProps('airlines')
db.enableIndex('airlines')
db.save('airlines', 'FYI-8098', 'this is a plain text flight')
db.search('airlines', 'text')
or in a Map/Reduce scenario:
db.addSearch('airlines', 'text').map('Riak.mapValues').run()
Please report issues here.
Run this in the main directory to compile coffeescript to javascript as you go:
cake dev
Checkout the spec
folder.
Test with cake test
. Requires Vows 0.5.2.