An Elixir wrapper for riak-erlang-client.
What is Elixir?
From http://elixir-lang.org/:
Elixir is a functional, meta-programming aware language built on top of the Erlang VM. It is a dynamic language with flexible syntax and macro support that leverages Erlang’s abilities to build concurrent, distributed and fault-tolerant applications with hot code upgrades.
Ok… But what’s wrong with Erlang?
Absolutely nothing.
Some have made the argument that Elixir’s ruby-like syntax is better than Erlang’s, but I don’t buy that. Elixir is still a functional language just like Erlang, and if you don’t like the way things are done in one, you probably won’t like the other. The choice between Elixir and Erlang is a matter of preference in how you like to express ideas through code.
I love the simplicity of Erlang’s syntax and data types for a lot of problems. For instance it makes sending raw messages from process to process look nice and clean no matter what machine each process happens to be running on. Elixir allows you to call erlang functions natively and throws all kinds of syntactic sugar on top of it allowing you to make additional abstractions with macros and the like. The tools that Elixir provides also make it easier to over-engineer things because it essentially gives you unlimited options when deciding how to tackle a problem, but some people like that flexibility.
Back to the Riak Elixir Client: Usage
I’ll leave the setup instructions out of this post as they are available in the README. Let’s get right into some usage options.
I intentionally stuck to functional programming dispatch style for this implementation. I wrote a separate client called Elixiak that uses object-oriented style dispatch (which is generally frowned upon by the Elixir community from what I understand, but it’s still a matter of preference).
Connect to Riak via Protobufs
Riak.start
Riak.configure(host: '127.0.0.1', port: 8087)
The start
function starts the OTP application, and configure
sends a message to the OTP server running locally which starts the protobuf link with your Riak cluster.
CRUD operations
u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
|> Riak.put
u = Riak.find "user", "my_key"
u = u.data("Updated Data")
|> Riak.put
Riak.delete "user", key
The |>
takes the result of the previous function and passes it as the first argument to the next function. You can also pass u
to Riak.put like so: Riak.put u
Bucket props
Riak.Bucket.put "user", [{:notfound_ok, false}])
{:ok, props} = Riak.Bucket.get "user"
Riak.Bucket.reset "user"
:ok
is an Elixir atom, the equivalent of ok
in Erlang.
User metadata
u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
|> RObj.put_metadata({"my_key", "my_value"})
|> RObj.put_metadata({"my_key2", "my_value2"})
|> Riak.put
Secondary indexes
u = RObj.create(bucket: "user", key: key, data: "Drew Kerrigan")
|> RObj.put_index({:binary_index, "first_name"}, ["Drew"])
|> RObj.put_index({:binary_index, "last_name"}, ["Kerrigan"])
|> Riak.put
index = {:binary_index, "first_name"}
query = "Drew"
options = []
{keys, terms, continuation} = Riak.Index.query("user", index, query, options)
The call to Riak.Index.query
above is a good example of pattern matching in Elixir. The variables keys
, terms
, and continuation
are bound to values returned in that order as a tuple by the query
function. terms
and continuation
are relatively new features of Riak, introduced in Riak 1.4. They allow for additional information about what terms were matched in your query as well as the ability to do pagination by specifying rows
and start
parameters in the options
variable.
Links
u = RObj.create(bucket: "user", key: "my_key", data: "Drew Kerrigan")
|> RObj.put_link("my_tag", "user", "drew1")
|> RObj.put_link("my_tag", "user", "drew2")
|> Riak.put
Siblings
If you have the bucket property allow_mult
set, you will likely encounter siblings, below is a quick example of resolving them.
user_value_list = Riak.find "user", key
if (is_list(user_value_list)) do
Riak.resolve("user", key, 1)
end
The 1
parameter corresponds to the nth entry in the list of siblings contained in user_value_list
.
Counters, Yokozuna, and Map/Reduce
The code for these features is written, but the riak-erlang-client functionality for counters and yokozuna is still in active development and therefore unstable, so I’m waiting a bit before endorsing their use with this client library.
Map/reduce functionality also exists, but I was unable to resolve a few bugs related to syntax with anonymous map and reduce functions. Feel free to try it out though, it should work fine with non-anonymous map/reduce functions in which the code already exists on your Riak nodes.
That’s it!
More detailed usage examples can be found in the tests: http://github.com/drewkerrigan/riak-elixir-client/blob/master/test/riak_test.exs
There will be another blog post on the way discussing the object-oriented style dispatch client library called Elixiak: http://github.com/drewkerrigan/elixiak