├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── THANKS ├── config └── config.exs ├── doc ├── 404.html ├── Riak.Bucket.Type.html ├── Riak.Bucket.html ├── Riak.CRDT.Counter.html ├── Riak.CRDT.Flag.html ├── Riak.CRDT.HyperLogLog.html ├── Riak.CRDT.Map.html ├── Riak.CRDT.Register.html ├── Riak.CRDT.Set.html ├── Riak.CRDT.html ├── Riak.Connection.html ├── Riak.Index.html ├── Riak.Mapred.Bucket.html ├── Riak.Mapred.html ├── Riak.Object.html ├── Riak.Pool.html ├── Riak.Search.Index.html ├── Riak.Search.Schema.html ├── Riak.Search.html ├── Riak.Timeseries.html ├── Riak.html ├── api-reference.html ├── dist │ ├── app-268c9dc719.css │ ├── app-4988ddd06b.js │ ├── app-4988ddd06b.js.map │ └── sidebar_items-8bfd31e79d.js ├── fonts │ ├── icomoon.eot │ ├── icomoon.svg │ ├── icomoon.ttf │ └── icomoon.woff └── index.html ├── examples └── exploriak │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── exploriak │ ├── lib │ └── exploriak.ex │ ├── mix.exs │ ├── mix.lock │ └── test │ ├── exploriak_test.exs │ └── test_helper.exs ├── lib ├── riak.ex └── riak │ ├── bucket.ex │ ├── connection.ex │ ├── crdt.ex │ ├── crdt │ ├── counter.ex │ ├── flag.ex │ ├── hll.ex │ ├── map.ex │ ├── register.ex │ └── set.ex │ ├── index.ex │ ├── map_reduce.ex │ ├── object.ex │ ├── pool.ex │ ├── search.ex │ └── timeseries.ex ├── mix.exs ├── mix.lock └── test ├── bucket_test.exs ├── crdt_counter_test.exs ├── crdt_test.exs ├── flag_test.exs ├── hll_test.exs ├── map_test.exs ├── pool_test.exs ├── riak_test.exs ├── set_test.exs ├── test_helper.exs └── timeseries_test.exs /.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | deps 3 | ebin -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: elixir 2 | elixir: 3 | - 1.4.1 4 | notifications: 5 | recipients: 6 | - drew@kerrigan.io 7 | otp_release: 8 | - 19.2 9 | env: 10 | - MIX_ENV=test 11 | services: 12 | - riak 13 | script: mix test --exclude riak2 --exclude riakts 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014-2015 Drew Kerrigan 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | All of the files in this project are under the project-wide license 16 | unless they are otherwise marked. 17 | Apache License 18 | Version 2.0, January 2004 19 | http://www.apache.org/licenses/ 20 | 21 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 22 | 23 | 1. Definitions. 24 | 25 | "License" shall mean the terms and conditions for use, reproduction, 26 | and distribution as defined by Sections 1 through 9 of this document. 27 | 28 | "Licensor" shall mean the copyright owner or entity authorized by 29 | the copyright owner that is granting the License. 30 | 31 | "Legal Entity" shall mean the union of the acting entity and all 32 | other entities that control, are controlled by, or are under common 33 | control with that entity. For the purposes of this definition, 34 | "control" means (i) the power, direct or indirect, to cause the 35 | direction or management of such entity, whether by contract or 36 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 37 | outstanding shares, or (iii) beneficial ownership of such entity. 38 | 39 | "You" (or "Your") shall mean an individual or Legal Entity 40 | exercising permissions granted by this License. 41 | 42 | "Source" form shall mean the preferred form for making modifications, 43 | including but not limited to software source code, documentation 44 | source, and configuration files. 45 | 46 | "Object" form shall mean any form resulting from mechanical 47 | transformation or translation of a Source form, including but 48 | not limited to compiled object code, generated documentation, 49 | and conversions to other media types. 50 | 51 | "Work" shall mean the work of authorship, whether in Source or 52 | Object form, made available under the License, as indicated by a 53 | copyright notice that is included in or attached to the work 54 | (an example is provided in the Appendix below). 55 | 56 | "Derivative Works" shall mean any work, whether in Source or Object 57 | form, that is based on (or derived from) the Work and for which the 58 | editorial revisions, annotations, elaborations, or other modifications 59 | represent, as a whole, an original work of authorship. For the purposes 60 | of this License, Derivative Works shall not include works that remain 61 | separable from, or merely link (or bind by name) to the interfaces of, 62 | the Work and Derivative Works thereof. 63 | 64 | "Contribution" shall mean any work of authorship, including 65 | the original version of the Work and any modifications or additions 66 | to that Work or Derivative Works thereof, that is intentionally 67 | submitted to Licensor for inclusion in the Work by the copyright owner 68 | or by an individual or Legal Entity authorized to submit on behalf of 69 | the copyright owner. For the purposes of this definition, "submitted" 70 | means any form of electronic, verbal, or written communication sent 71 | to the Licensor or its representatives, including but not limited to 72 | communication on electronic mailing lists, source code control systems, 73 | and issue tracking systems that are managed by, or on behalf of, the 74 | Licensor for the purpose of discussing and improving the Work, but 75 | excluding communication that is conspicuously marked or otherwise 76 | designated in writing by the copyright owner as "Not a Contribution." 77 | 78 | "Contributor" shall mean Licensor and any individual or Legal Entity 79 | on behalf of whom a Contribution has been received by Licensor and 80 | subsequently incorporated within the Work. 81 | 82 | 2. Grant of Copyright License. Subject to the terms and conditions of 83 | this License, each Contributor hereby grants to You a perpetual, 84 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 85 | copyright license to reproduce, prepare Derivative Works of, 86 | publicly display, publicly perform, sublicense, and distribute the 87 | Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of 90 | this License, each Contributor hereby grants to You a perpetual, 91 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 92 | (except as stated in this section) patent license to make, have made, 93 | use, offer to sell, sell, import, and otherwise transfer the Work, 94 | where such license applies only to those patent claims licensable 95 | by such Contributor that are necessarily infringed by their 96 | Contribution(s) alone or by combination of their Contribution(s) 97 | with the Work to which such Contribution(s) was submitted. If You 98 | institute patent litigation against any entity (including a 99 | cross-claim or counterclaim in a lawsuit) alleging that the Work 100 | or a Contribution incorporated within the Work constitutes direct 101 | or contributory patent infringement, then any patent licenses 102 | granted to You under this License for that Work shall terminate 103 | as of the date such litigation is filed. 104 | 105 | 4. Redistribution. You may reproduce and distribute copies of the 106 | Work or Derivative Works thereof in any medium, with or without 107 | modifications, and in Source or Object form, provided that You 108 | meet the following conditions: 109 | 110 | (a) You must give any other recipients of the Work or 111 | Derivative Works a copy of this License; and 112 | 113 | (b) You must cause any modified files to carry prominent notices 114 | stating that You changed the files; and 115 | 116 | (c) You must retain, in the Source form of any Derivative Works 117 | that You distribute, all copyright, patent, trademark, and 118 | attribution notices from the Source form of the Work, 119 | excluding those notices that do not pertain to any part of 120 | the Derivative Works; and 121 | 122 | (d) If the Work includes a "NOTICE" text file as part of its 123 | distribution, then any Derivative Works that You distribute must 124 | include a readable copy of the attribution notices contained 125 | within such NOTICE file, excluding those notices that do not 126 | pertain to any part of the Derivative Works, in at least one 127 | of the following places: within a NOTICE text file distributed 128 | as part of the Derivative Works; within the Source form or 129 | documentation, if provided along with the Derivative Works; or, 130 | within a display generated by the Derivative Works, if and 131 | wherever such third-party notices normally appear. The contents 132 | of the NOTICE file are for informational purposes only and 133 | do not modify the License. You may add Your own attribution 134 | notices within Derivative Works that You distribute, alongside 135 | or as an addendum to the NOTICE text from the Work, provided 136 | that such additional attribution notices cannot be construed 137 | as modifying the License. 138 | 139 | You may add Your own copyright statement to Your modifications and 140 | may provide additional or different license terms and conditions 141 | for use, reproduction, or distribution of Your modifications, or 142 | for any such Derivative Works as a whole, provided Your use, 143 | reproduction, and distribution of the Work otherwise complies with 144 | the conditions stated in this License. 145 | 146 | 5. Submission of Contributions. Unless You explicitly state otherwise, 147 | any Contribution intentionally submitted for inclusion in the Work 148 | by You to the Licensor shall be under the terms and conditions of 149 | this License, without any additional terms or conditions. 150 | Notwithstanding the above, nothing herein shall supersede or modify 151 | the terms of any separate license agreement you may have executed 152 | with Licensor regarding such Contributions. 153 | 154 | 6. Trademarks. This License does not grant permission to use the trade 155 | names, trademarks, service marks, or product names of the Licensor, 156 | except as required for reasonable and customary use in describing the 157 | origin of the Work and reproducing the content of the NOTICE file. 158 | 159 | 7. Disclaimer of Warranty. Unless required by applicable law or 160 | agreed to in writing, Licensor provides the Work (and each 161 | Contributor provides its Contributions) on an "AS IS" BASIS, 162 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 163 | implied, including, without limitation, any warranties or conditions 164 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 165 | PARTICULAR PURPOSE. You are solely responsible for determining the 166 | appropriateness of using or redistributing the Work and assume any 167 | risks associated with Your exercise of permissions under this License. 168 | 169 | 8. Limitation of Liability. In no event and under no legal theory, 170 | whether in tort (including negligence), contract, or otherwise, 171 | unless required by applicable law (such as deliberate and grossly 172 | negligent acts) or agreed to in writing, shall any Contributor be 173 | liable to You for damages, including any direct, indirect, special, 174 | incidental, or consequential damages of any character arising as a 175 | result of this License or out of the use or inability to use the 176 | Work (including but not limited to damages for loss of goodwill, 177 | work stoppage, computer failure or malfunction, or any and all 178 | other commercial damages or losses), even if such Contributor 179 | has been advised of the possibility of such damages. 180 | 181 | 9. Accepting Warranty or Additional Liability. While redistributing 182 | the Work or Derivative Works thereof, You may choose to offer, 183 | and charge a fee for, acceptance of support, warranty, indemnity, 184 | or other liability obligations and/or rights consistent with this 185 | License. However, in accepting such obligations, You may act only 186 | on Your own behalf and on Your sole responsibility, not on behalf 187 | of any other Contributor, and only if You agree to indemnify, 188 | defend, and hold each Contributor harmless for any liability 189 | incurred by, or claims asserted against, such Contributor by reason 190 | of your accepting any such warranty or additional liability. 191 | 192 | END OF TERMS AND CONDITIONS 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Riak Elixir Client 2 | [![Build Status](https://travis-ci.org/drewkerrigan/riak-elixir-client.svg?branch=master)](https://travis-ci.org/drewkerrigan/riak-elixir-client) 3 | [![Hex version](https://img.shields.io/hexpm/v/riak.svg "Hex Version")](https://hex.pm/packages/riak) 4 | ![Hex downloads](https://img.shields.io/hexpm/dt/riak.svg "Hex Downloads") 5 | [![Stories in Ready](https://badge.waffle.io/drewkerrigan/riak-elixir-client.png?label=ready&title=Ready)](https://waffle.io/drewkerrigan/riak-elixir-client) 6 | 7 | A Riak client written in Elixir. Now includes connection pooling with [pooler](http://github.com/seth/pooler) and a variety of other improvements from [riex](https://github.com/edgurgel/riex). 8 | 9 | ## Setup 10 | 11 | ### Prerequisites 12 | 13 | * Riak 2.0+ 14 | * Elixir 1.0+ 15 | 16 | #### In an Elixir application 17 | 18 | Add the following to mix.exs 19 | 20 | ```elixir 21 | ... 22 | def application do 23 | [ applications: [ :riak ]] 24 | end 25 | ... 26 | defp deps do 27 | [ {:riak, "~> 1.1.6"} ] 28 | end 29 | ... 30 | ``` 31 | 32 | ## Usage 33 | 34 | ### Establishing a Riak connection 35 | 36 | ```elixir 37 | {:ok, pid} = Riak.Connection.start_link('127.0.0.1', 8087) # Default values 38 | ``` 39 | 40 | ### Connection Pooling 41 | 42 | Most functions in this module can be called by passing the pid of the established connection or using a pool of connections (provided by pooler). Define pools by using the group `riak`. Following is an example `config/config.exs`: 43 | 44 | ```elixir 45 | config :pooler, pools: 46 | [ 47 | [ 48 | name: :riaklocal1, 49 | group: :riak, 50 | max_count: 10, 51 | init_count: 5, 52 | start_mfa: { Riak.Connection, :start_link, [] } 53 | ], [ 54 | name: :riaklocal2, 55 | group: :riak, 56 | max_count: 15, 57 | init_count: 2, 58 | start_mfa: { Riak.Connection, :start_link, ['127.0.0.1', 9090] } 59 | ] 60 | ] 61 | ``` 62 | 63 | For an example using this functionality with a local Riak instance, check [`config/config.exs`](https://github.com/drewkerrigan/riak-elixir-client/blob/master/config/config.exs). More information about Elixir configuration can be found on [http://elixir-lang.org](http://elixir-lang.org): [Application environment and configuration](http://elixir-lang.org/getting-started/mix-otp/distributed-tasks-and-configuration.html#application-environment-and-configuration). 64 | 65 | Once a pool configuration is properly defined in a project, calls to Riak can omit the pid. For example: 66 | 67 | This call uses a pid from the pool of connections provided by pooler: 68 | 69 | ```elixir 70 | Riak.delete("user", key) 71 | ``` 72 | 73 | This call requires a pid obtained by first calling `Riak.Connection.start_link`: 74 | 75 | ```elixir 76 | Riak.delete(pid, "user", key) 77 | ``` 78 | 79 | ### Save a value 80 | 81 | ```elixir 82 | o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo") 83 | Riak.put(pid, o) 84 | ``` 85 | 86 | ### Find an object 87 | 88 | ```elixir 89 | o = Riak.find(pid, "user", "my_key") 90 | ``` 91 | 92 | ### Update an object 93 | 94 | ```elixir 95 | o = %{o | data: "Something Else"} 96 | Riak.put(pid, o) 97 | ``` 98 | 99 | ### Delete an object 100 | 101 | Using key 102 | 103 | ```elixir 104 | Riak.delete(pid, "user", key) 105 | ``` 106 | 107 | Using object 108 | 109 | ```elixir 110 | Riak.delete(pid, o) 111 | ``` 112 | 113 | ### Timeseries 114 | 115 | Riak Timeseries functionality is available in [TS 1.3.1 releases of Riak](http://docs.basho.com/riak/ts/1.3.1/downloads/) and greater. 116 | 117 | #### Setup 118 | 119 | Create a table: 120 | 121 | ``` 122 | riak-admin bucket-type create GeoCheckin '{"props":{"table_def": "CREATE TABLE GeoCheckin (region VARCHAR NOT NULL, state VARCHAR NOT NULL, time TIMESTAMP NOT NULL, weather VARCHAR NOT NULL, temperature DOUBLE, PRIMARY KEY ((region, state, QUANTUM(time, 15, 'm')), region, state, time))"}}' 123 | riak-admin bucket-type activate GeoCheckin 124 | ``` 125 | 126 | #### Insert Rows 127 | 128 | ``` 129 | Riak.Timeseries.put("GeoCheckin", [ 130 | {"region1", "state1", 25, "hot", 23.0}, 131 | {"region2", "state99", 26, "windy", 19.0} 132 | ]) 133 | > :ok 134 | ``` 135 | 136 | #### Get a row by primary key 137 | 138 | ``` 139 | Riak.Timeseries.get("GeoCheckin", ["region1", "state1", 25]) 140 | > {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]} 141 | ``` 142 | 143 | #### Get all rows 144 | 145 | *Note*: This is a very expensive operation for a loaded cluster 146 | 147 | ``` 148 | Riak.Timeseries.list!("GeoCheckin") 149 | > [{"region1", "state1", 25, "hot", 23.0}, {"region2", "state99", 26, "windy", 19.0}] 150 | ``` 151 | 152 | #### Delete a row 153 | 154 | ``` 155 | Riak.Timeseries.delete("GeoCheckin", ["region2", "state99", 26]) 156 | > :ok 157 | ``` 158 | 159 | #### Query 160 | 161 | ``` 162 | Riak.Timeseries.query("select * from GeoCheckin where time > 24 and time < 26 and region = 'region1' and state = 'state1'") 163 | > {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]} 164 | ``` 165 | 166 | ### Datatypes 167 | 168 | Riak Datatypes (a.k.a. CRDTs) are avaiable in [Riak versions 2.0](http://basho.com/introducing-riak-2-0/) and greater. The types included are: maps, sets, counters, registers and flags. 169 | 170 | #### Setup 171 | 172 | Datatypes require the use of bucket-types. Maps, sets, counters, and hyper-log-logs can be used as top-level bucket-type datatypes; Registers and flags may only be used within maps. 173 | 174 | The following examples assume the presence of 4 datatype enabled bucket-types. You can create these bucket-types by running the following commands on a single Riak node in your cluster: 175 | 176 | Bucket-Type: `counters` 177 | 178 | ``` 179 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}' 180 | riak-admin bucket-type activate counters 181 | ``` 182 | 183 | Bucket-Type: `sets` 184 | 185 | ``` 186 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}' 187 | riak-admin bucket-type activate sets 188 | ``` 189 | 190 | Bucket-Type: `maps` 191 | 192 | ``` 193 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}' 194 | riak-admin bucket-type activate maps 195 | ``` 196 | 197 | Bucket-Type: `hll` 198 | 199 | ``` 200 | riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}' 201 | riak-admin bucket-type activate hll 202 | ``` 203 | 204 | #### Counters 205 | 206 | Create a counter (`alias Riak.CRDT.Counter`): 207 | 208 | ```elixir 209 | Counter.new 210 | |> Counter.increment 211 | |> Counter.increment(2) 212 | |> Riak.update("counters", "my_counter_bucket", "my_key") 213 | ``` 214 | 215 | Fetch a counter: 216 | 217 | ```elixir 218 | counter = Riak.find("counters", "my_counter_bucket", "my_key") 219 | |> Counter.value 220 | ``` 221 | 222 | `counter` will be 3. 223 | 224 | ***NOTE***: "Counter drift" is a possibility that needs to be accounted for with any distributed system such as Riak. The problem can manifest itself during failure states in either your applicaiton or Riak itself. If an increment operation fails from the client's point of view, there is not sufficient information available to know whether or not that call made it to zero or all of the replicas for that counter object. As such, if the client attempts to retry the increment after recieving something like a error code 500 from Riak, that counter object is at risk of drifting positive. Similarly if the client decides not to retry, that counter object is at risk of drifting negative. 225 | 226 | For these reasons, counters are only suggested for use-cases that can handle some (albeit small) amount of counter drift. Good examples of appropriate use-cases are: Facebook likes, Twitter retweet counts, Youtube view counts, etc. Some examples of poor use-cases for Riak counters are: bank account balances, anything related to money. It is possible to implement these types of solutions using Riak, but more client side logic is necessary. For an example of a client-side ledger with tunable retry options, check [github.com/drewkerrigan/riak-ruby-ledger](https://github.com/drewkerrigan/riak-ruby-ledger). Another approach could be the client-side implementation of a HAT (Highly Available Transaction) algorithm. 227 | 228 | #### Sets 229 | 230 | Create a set (`alias Riak.CRDT.Set`): 231 | 232 | ```elixir 233 | Set.new 234 | |> Set.put("foo") 235 | |> Set.put("bar") 236 | |> Riak.update("sets", "my_set_bucket", "my_key") 237 | ``` 238 | 239 | And fetch the set: 240 | 241 | ```elixir 242 | set = Riak.find("sets", "my_set_bucket", "my_key") 243 | |> Set.value 244 | ``` 245 | 246 | Where `set` is an `orddict`. 247 | 248 | #### Maps 249 | 250 | Maps handle binary keys with any other datatype (map, set, flag, register and counter). 251 | 252 | Create a map (`alias Riak.CRDT.Map`): 253 | 254 | ```elixir 255 | register = Register.new("some string") 256 | flag = Flag.new |> Flag.enable 257 | Map.new 258 | |> Map.put("k1", register) 259 | |> Map.put("k2", flag) 260 | |> Riak.update("maps", "my_map_bucket", "map_key") 261 | ``` 262 | 263 | And fetch the map: 264 | 265 | ```elixir 266 | map = Riak.find("maps", "my_map_bucket", key) |> Map.value 267 | ``` 268 | 269 | Where `map` is an `orddict`. 270 | 271 | #### Hyper Log Logs 272 | 273 | The use case for this type is counting distinct elements in a monotonic way. 274 | I think of it as like a counter for customers visited but once a customer visits 275 | the counter will never go up again. It also isn't possible to remove a element 276 | once it has been added to the log. 277 | 278 | Create a HLL (`alias Riak.CRDT.HyperLogLog`): 279 | 280 | ```elixir 281 | HyperLogLog.new 282 | |> HyperLogLog.add_element("foo") 283 | |> Riak.update("hll", "my_hll_bucket", "hll_key") 284 | ``` 285 | 286 | And fetch the distinct count: 287 | 288 | ```elixir 289 | hll = Riak.find("hll", "my_hll_bucket", "hll_key") |> HLL.value 290 | ``` 291 | 292 | Where `hll` is an `integer`. 293 | 294 | ## Examples 295 | 296 | Check the `examples/` directory for a few example elixir applications using the riak client. 297 | 298 | For more functionality, check `test/` directory. 299 | 300 | ## Tests 301 | 302 | ``` 303 | MIX_ENV=test mix do deps.get, test 304 | ``` 305 | 306 | ***NOTE:*** If you see errors related to `{:error, :nil_object}`, Ensure that you have created and activated the below `map`, `set`, and `counter` bucket types. 307 | 308 | 309 | *Note* 310 | 311 | The creation of the following CRDT bucket-types is a prerequisite for passing the CRDT tests. 312 | 313 | ``` 314 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}' 315 | riak-admin bucket-type activate maps 316 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}' 317 | riak-admin bucket-type activate sets 318 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}' 319 | riak-admin bucket-type activate counters 320 | riak-admin bucket-type create hll '{"props":{"datatype":"hll"}}' 321 | riak-admin bucket-type activate hll 322 | ``` 323 | 324 | *Note* 325 | 326 | The creation of this Timeseries table is a prerequisite for passing the Timeseries tests. 327 | 328 | ``` 329 | riak-admin bucket-type create GeoCheckin '{"props":{"table_def": "CREATE TABLE GeoCheckin (region VARCHAR NOT NULL, state VARCHAR NOT NULL, time TIMESTAMP NOT NULL, weather VARCHAR NOT NULL, temperature DOUBLE, PRIMARY KEY ((region, state, QUANTUM(time, 15, 'm')), region, state, time))"}}' 330 | riak-admin bucket-type activate GeoCheckin 331 | ``` 332 | 333 | 334 | ## License 335 | 336 | Copyright 2017 Drew Kerrigan. 337 | Copyright 2014 Eduardo Gurgel. 338 | 339 | Licensed under the Apache License, Version 2.0 (the "License"); 340 | you may not use this file except in compliance with the License. 341 | You may obtain a copy of the License at 342 | 343 | http://www.apache.org/licenses/LICENSE-2.0 344 | 345 | Unless required by applicable law or agreed to in writing, software 346 | distributed under the License is distributed on an "AS IS" BASIS, 347 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 348 | See the License for the specific language governing permissions and 349 | limitations under the License. 350 | -------------------------------------------------------------------------------- /THANKS: -------------------------------------------------------------------------------- 1 | The following people have contributed to the Riak Elixir client: 2 | 3 | THANKS 4 | ------ 5 | 6 | Drew Kerrigan 7 | Eduardo Gurgel 8 | Eric Liaw 9 | Justin McNally 10 | Lucas Allan 11 | Martin Gausby 12 | Paulo Almeida -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | use Mix.Config 2 | 3 | config :pooler, pools: 4 | [ 5 | [ 6 | name: :riaklocal, 7 | group: :riak, 8 | max_count: 10, 9 | init_count: 5, 10 | start_mfa: { Riak.Connection, :start_link, [] } 11 | ] 12 | ] 13 | -------------------------------------------------------------------------------- /doc/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 404 – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

Page not found

66 | 67 |

Sorry, but the page you were trying to get to, does not exist. You 68 | may want to try searching this site using the sidebar or using our 69 | API Reference page to find what 70 | you were looking for.

71 | 72 | 85 |
86 |
87 |
88 |
89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.Counter.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT.Counter – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT.Counter 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Counter data-type on Riak 2.0.

79 | 80 |
81 | 82 | 83 | 84 |
85 |

86 | 87 | 88 | Link to this section 89 | 90 | Summary 91 |

92 | 93 | 94 | 95 |
96 |

97 | Functions 98 |

99 |
100 | 103 | 104 |

Decrement a counter on the amount defaulting in 1

105 |
106 | 107 |
108 |
109 | 112 | 113 |

Increment a counter on the amount defaulting in 1

114 |
115 | 116 |
117 |
118 |
119 | new() 120 |
121 | 122 |

Create a new counter

123 |
124 | 125 |
126 |
127 |
128 | value(counter) 129 |
130 | 131 |

Get the original value as a number

132 |
133 | 134 |
135 | 136 |
137 | 138 | 139 | 140 | 141 |
142 | 143 | 144 | 145 | 146 | 147 |
148 |

149 | 150 | 151 | Link to this section 152 | 153 | Functions 154 |

155 |
156 | 157 | 158 | 159 |
160 | 161 | 162 | Link to this function 163 | 164 | decrement(counter, amount \\ 1) 165 | 166 | 167 | 168 | View Source 169 | 170 | 171 | 172 | 173 |
174 |
175 |

Decrement a counter on the amount defaulting in 1

176 | 177 |
178 |
179 |
180 | 181 | 182 | 183 |
184 | 185 | 186 | Link to this function 187 | 188 | increment(counter, amount \\ 1) 189 | 190 | 191 | 192 | View Source 193 | 194 | 195 | 196 | 197 |
198 |
199 |

Increment a counter on the amount defaulting in 1

200 | 201 |
202 |
203 |
204 | 205 |
206 | 207 | 208 | Link to this function 209 | 210 | new() 211 | 212 | 213 | 214 | View Source 215 | 216 | 217 | 218 | 219 |
220 |
221 |

Create a new counter

222 | 223 |
224 |
225 |
226 | 227 |
228 | 229 | 230 | Link to this function 231 | 232 | value(counter) 233 | 234 | 235 | 236 | View Source 237 | 238 | 239 | 240 | 241 |
242 |
243 |

Get the original value as a number

244 | 245 |
246 |
247 | 248 |
249 | 250 | 251 | 252 | 265 |
266 |
267 |
268 |
269 | 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.Flag.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT.Flag – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT.Flag 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Encapsulates a boolean datatype inside a CRDT.Map

79 | 80 |
81 | 82 | 83 | 84 |
85 |

86 | 87 | 88 | Link to this section 89 | 90 | Summary 91 |

92 | 93 | 94 | 95 |
96 |

97 | Functions 98 |

99 |
100 |
101 | disable(flag) 102 |
103 | 104 |

Turns the value to false

105 |
106 | 107 |
108 |
109 |
110 | enable(flag) 111 |
112 | 113 |

Turns the value to true

114 |
115 | 116 |
117 |
118 |
119 | new() 120 |
121 | 122 |

Creates a new flag container

123 |
124 | 125 |
126 |
127 |
128 | new(context) 129 |
130 | 131 |
132 |
133 |
134 | new(value, context) 135 |
136 | 137 |
138 |
139 |
140 | value(flag) 141 |
142 | 143 |

Extracts current value of flag

144 |
145 | 146 |
147 | 148 |
149 | 150 | 151 | 152 | 153 |
154 | 155 | 156 | 157 | 158 | 159 |
160 |

161 | 162 | 163 | Link to this section 164 | 165 | Functions 166 |

167 |
168 | 169 |
170 | 171 | 172 | Link to this function 173 | 174 | disable(flag) 175 | 176 | 177 | 178 | View Source 179 | 180 | 181 | 182 | 183 |
184 |
185 |

Turns the value to false

186 | 187 |
188 |
189 |
190 | 191 |
192 | 193 | 194 | Link to this function 195 | 196 | enable(flag) 197 | 198 | 199 | 200 | View Source 201 | 202 | 203 | 204 | 205 |
206 |
207 |

Turns the value to true

208 | 209 |
210 |
211 |
212 | 213 |
214 | 215 | 216 | Link to this function 217 | 218 | new() 219 | 220 | 221 | 222 | View Source 223 | 224 | 225 | 226 | 227 |
228 |
229 |

Creates a new flag container

230 | 231 |
232 |
233 |
234 | 235 |
236 | 237 | 238 | Link to this function 239 | 240 | new(context) 241 | 242 | 243 | 244 | View Source 245 | 246 | 247 | 248 | 249 |
250 |
251 | 252 |
253 |
254 |
255 | 256 |
257 | 258 | 259 | Link to this function 260 | 261 | new(value, context) 262 | 263 | 264 | 265 | View Source 266 | 267 | 268 | 269 | 270 |
271 |
272 | 273 |
274 |
275 |
276 | 277 |
278 | 279 | 280 | Link to this function 281 | 282 | value(flag) 283 | 284 | 285 | 286 | View Source 287 | 288 | 289 | 290 | 291 |
292 |
293 |

Extracts current value of flag

294 | 295 |
296 |
297 | 298 |
299 | 300 | 301 | 302 | 315 |
316 |
317 |
318 |
319 | 320 | 321 | 322 | 323 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.HyperLogLog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT.HyperLogLog – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT.HyperLogLog 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Encapsulates Riak HLL

79 | 80 |
81 | 82 | 83 | 84 |
85 |

86 | 87 | 88 | Link to this section 89 | 90 | Summary 91 |

92 | 93 | 94 | 95 |
96 |

97 | Functions 98 |

99 |
100 |
101 | add_element(hll, elm) 102 |
103 | 104 |

Adds a distinct element to the HLL

105 |
106 | 107 |
108 |
109 | 112 | 113 |

Adds a list of elements to the HLL

114 |
115 | 116 |
117 |
118 |
119 | new() 120 |
121 | 122 |

Creates a new hll

123 |
124 | 125 |
126 |
127 |
128 | value(hll) 129 |
130 | 131 |

Fetch the value of the HLL

132 |
133 | 134 |
135 | 136 |
137 | 138 | 139 | 140 | 141 |
142 | 143 | 144 | 145 | 146 | 147 |
148 |

149 | 150 | 151 | Link to this section 152 | 153 | Functions 154 |

155 |
156 | 157 |
158 | 159 | 160 | Link to this function 161 | 162 | add_element(hll, elm) 163 | 164 | 165 | 166 | View Source 167 | 168 | 169 | 170 | 171 |
172 |
173 |

Adds a distinct element to the HLL

174 | 175 |
176 |
177 |
178 | 179 |
180 | 181 | 182 | Link to this function 183 | 184 | add_elements(hll, list) 185 | 186 | 187 | 188 | View Source 189 | 190 | 191 | 192 | 193 |
194 |
195 |

Adds a list of elements to the HLL

196 | 197 |
198 |
199 |
200 | 201 |
202 | 203 | 204 | Link to this function 205 | 206 | new() 207 | 208 | 209 | 210 | View Source 211 | 212 | 213 | 214 | 215 |
216 |
217 |

Creates a new hll

218 | 219 |
220 |
221 |
222 | 223 |
224 | 225 | 226 | Link to this function 227 | 228 | value(hll) 229 | 230 | 231 | 232 | View Source 233 | 234 | 235 | 236 | 237 |
238 |
239 |

Fetch the value of the HLL

240 | 241 |
242 |
243 | 244 |
245 | 246 | 247 | 248 | 261 |
262 |
263 |
264 |
265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.Register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT.Register – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT.Register 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Encapsulates a binary data to be used on CRDT.Map’s

79 | 80 |
81 | 82 | 83 | 84 |
85 |

86 | 87 | 88 | Link to this section 89 | 90 | Summary 91 |

92 | 93 | 94 | 95 |
96 |

97 | Functions 98 |

99 |
100 |
101 | new() 102 |
103 | 104 |

Creates a new register

105 |
106 | 107 |
108 |
109 |
110 | new(value) 111 |
112 | 113 |

Creates a new register with the initial value

114 |
115 | 116 |
117 |
118 |
119 | set(register, value) 120 |
121 | 122 |

Set the value on the register

123 |
124 | 125 |
126 |
127 |
128 | value(register) 129 |
130 | 131 |

Extracts current value of register

132 |
133 | 134 |
135 | 136 |
137 | 138 | 139 | 140 | 141 |
142 | 143 | 144 | 145 | 146 | 147 |
148 |

149 | 150 | 151 | Link to this section 152 | 153 | Functions 154 |

155 |
156 | 157 |
158 | 159 | 160 | Link to this function 161 | 162 | new() 163 | 164 | 165 | 166 | View Source 167 | 168 | 169 | 170 | 171 |
172 |
173 |

Creates a new register

174 | 175 |
176 |
177 |
178 | 179 |
180 | 181 | 182 | Link to this function 183 | 184 | new(value) 185 | 186 | 187 | 188 | View Source 189 | 190 | 191 | 192 | 193 |
194 |
195 |

Creates a new register with the initial value

196 | 197 |
198 |
199 |
200 | 201 |
202 | 203 | 204 | Link to this function 205 | 206 | set(register, value) 207 | 208 | 209 | 210 | View Source 211 | 212 | 213 | 214 | 215 |
216 |
217 |

Set the value on the register

218 | 219 |
220 |
221 |
222 | 223 |
224 | 225 | 226 | Link to this function 227 | 228 | value(register) 229 | 230 | 231 | 232 | View Source 233 | 234 | 235 | 236 | 237 |
238 |
239 |

Extracts current value of register

240 | 241 |
242 |
243 | 244 |
245 | 246 | 247 | 248 | 261 |
262 |
263 |
264 |
265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.Set.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT.Set – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT.Set 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Encapsulates riakc_set using elixir Set functions as interface. 79 | It accepts only binary as items.

80 | 81 |
82 | 83 | 84 | 85 |
86 |

87 | 88 | 89 | Link to this section 90 | 91 | Summary 92 |

93 | 94 | 95 | 96 |
97 |

98 | Functions 99 |

100 |
101 |
102 | delete(set, value) 103 |
104 | 105 |

Delete value on set

106 |
107 | 108 |
109 |
110 |
111 | member?(set, value) 112 |
113 | 114 |

Checks if set contains value

115 |
116 | 117 |
118 |
119 |
120 | new() 121 |
122 | 123 |

Create an empty set

124 |
125 | 126 |
127 |
128 |
129 | new(context) 130 |
131 | 132 |
133 |
134 |
135 | new(value, context) 136 |
137 | 138 |
139 |
140 |
141 | put(set, value) 142 |
143 | 144 |

Insert value on set

145 |
146 | 147 |
148 |
149 |
150 | size(set) 151 |
152 | 153 |

Returns the number of elements in set

154 |
155 | 156 |
157 |
158 |
159 | value(set) 160 |
161 | 162 |

Get original value as an ordset

163 |
164 | 165 |
166 | 167 |
168 | 169 | 170 | 171 | 172 |
173 | 174 | 175 | 176 | 177 | 178 |
179 |

180 | 181 | 182 | Link to this section 183 | 184 | Functions 185 |

186 |
187 | 188 |
189 | 190 | 191 | Link to this function 192 | 193 | delete(set, value) 194 | 195 | 196 | 197 | View Source 198 | 199 | 200 | 201 | 202 |
203 |
204 |

Delete value on set

205 | 206 |
207 |
208 |
209 | 210 |
211 | 212 | 213 | Link to this function 214 | 215 | member?(set, value) 216 | 217 | 218 | 219 | View Source 220 | 221 | 222 | 223 | 224 |
225 |
226 |

Checks if set contains value.

227 | 228 |
229 |
230 |
231 | 232 |
233 | 234 | 235 | Link to this function 236 | 237 | new() 238 | 239 | 240 | 241 | View Source 242 | 243 | 244 | 245 | 246 |
247 |
248 |

Create an empty set

249 | 250 |
251 |
252 |
253 | 254 |
255 | 256 | 257 | Link to this function 258 | 259 | new(context) 260 | 261 | 262 | 263 | View Source 264 | 265 | 266 | 267 | 268 |
269 |
270 | 271 |
272 |
273 |
274 | 275 |
276 | 277 | 278 | Link to this function 279 | 280 | new(value, context) 281 | 282 | 283 | 284 | View Source 285 | 286 | 287 | 288 | 289 |
290 |
291 | 292 |
293 |
294 |
295 | 296 |
297 | 298 | 299 | Link to this function 300 | 301 | put(set, value) 302 | 303 | 304 | 305 | View Source 306 | 307 | 308 | 309 | 310 |
311 |
312 |

Insert value on set

313 | 314 |
315 |
316 |
317 | 318 |
319 | 320 | 321 | Link to this function 322 | 323 | size(set) 324 | 325 | 326 | 327 | View Source 328 | 329 | 330 | 331 | 332 |
333 |
334 |

Returns the number of elements in set

335 | 336 |
337 |
338 |
339 | 340 |
341 | 342 | 343 | Link to this function 344 | 345 | value(set) 346 | 347 | 348 | 349 | View Source 350 | 351 | 352 | 353 | 354 |
355 |
356 |

Get original value as an ordset

357 | 358 |
359 |
360 | 361 |
362 | 363 | 364 | 365 | 378 |
379 |
380 |
381 |
382 | 383 | 384 | 385 | 386 | -------------------------------------------------------------------------------- /doc/Riak.CRDT.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.CRDT – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.CRDT 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

Common CRDT module

79 | 80 |
81 | 82 | 83 | 84 |
85 |

86 | 87 | 88 | Link to this section 89 | 90 | Summary 91 |

92 | 93 | 94 | 95 |
96 |

97 | Functions 98 |

99 |
100 |
101 | type(value) 102 |
103 | 104 |
105 | 106 |
107 | 108 | 109 | 110 | 111 |
112 | 113 | 114 | 115 | 116 | 117 |
118 |

119 | 120 | 121 | Link to this section 122 | 123 | Functions 124 |

125 |
126 | 127 |
128 | 129 | 130 | Link to this function 131 | 132 | type(value) 133 | 134 | 135 | 136 | View Source 137 | 138 | 139 | 140 | 141 |
142 |
143 | 144 |
145 |
146 | 147 |
148 | 149 | 150 | 151 | 164 |
165 |
166 |
167 |
168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /doc/Riak.Connection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Connection – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Connection 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |

Create a process to talk with the riak server on host:port

100 |
101 | 102 |
103 |
104 | 107 | 108 |

Create a linked process to talk with the riak server on host:port

109 |
110 | 111 |
112 | 113 |
114 | 115 | 116 | 117 | 118 |
119 | 120 | 121 | 122 | 123 | 124 |
125 |

126 | 127 | 128 | Link to this section 129 | 130 | Functions 131 |

132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
141 | 142 | 143 | Link to this function 144 | 145 | start(host \\ '127.0.0.1', port \\ 8087, args \\ []) 146 | 147 | 148 | 149 | View Source 150 | 151 | 152 | 153 | 154 |
155 |
156 |

Create a process to talk with the riak server on host:port.

157 | 158 |
159 |
160 |
161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 |
169 | 170 | 171 | Link to this function 172 | 173 | start_link(host \\ '127.0.0.1', port \\ 8087, args \\ []) 174 | 175 | 176 | 177 | View Source 178 | 179 | 180 | 181 | 182 |
183 |
184 |

Create a linked process to talk with the riak server on host:port.

185 | 186 |
187 |
188 | 189 |
190 | 191 | 192 | 193 | 206 |
207 |
208 |
209 |
210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /doc/Riak.Index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Index – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Index 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |
100 |
101 | 104 | 105 |

@deprecated There are performance issues with 2i, please use another query strategy. 106 | Execute a secondary index equality query

107 |
108 | 109 |
110 |
111 | 114 | 115 |

@deprecated There are performance issues with 2i, please use another query strategy. 116 | Execute a secondary index range query

117 |
118 | 119 |
120 | 121 |
122 | 123 | 124 | 125 | 126 |
127 | 128 | 129 | 130 | 131 | 132 |
133 |

134 | 135 | 136 | Link to this section 137 | 138 | Functions 139 |

140 |
141 | 142 |
143 | 144 | 145 | Link to this function 146 | 147 | query(bucket, arg, key, opts) 148 | 149 | 150 | 151 | View Source 152 | 153 | 154 | 155 | 156 |
157 |
158 | 159 |
160 |
161 |
162 | 163 |
164 | 165 | 166 | Link to this function 167 | 168 | query(pid, bucket, startkey, key, opts) 169 | 170 | 171 | 172 | View Source 173 | 174 | 175 | 176 | 177 |
178 |
179 |

@deprecated There are performance issues with 2i, please use another query strategy. 180 | Execute a secondary index equality query.

181 | 182 |
183 |
184 |
185 | 186 |
187 | 188 | 189 | Link to this function 190 | 191 | query(pid, bucket, arg, startkey, endkey, opts) 192 | 193 | 194 | 195 | View Source 196 | 197 | 198 | 199 | 200 |
201 |
202 |

@deprecated There are performance issues with 2i, please use another query strategy. 203 | Execute a secondary index range query.

204 | 205 |
206 |
207 | 208 |
209 | 210 | 211 | 212 | 225 |
226 |
227 |
228 |
229 | 230 | 231 | 232 | 233 | -------------------------------------------------------------------------------- /doc/Riak.Mapred.Bucket.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Mapred.Bucket – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Mapred.Bucket 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |
100 |
101 | 104 | 105 |

@deprecated There are performance issues with MapReduce, please use another query strategy. 106 | Perform a MapReduce job across the cluster using an entire bucket as the input. 107 | See the MapReduce documentation for explanation of behavior

108 |
109 | 110 |
111 |
112 | 115 | 116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 |
124 | 125 | 126 | 127 | 128 | 129 |
130 |

131 | 132 | 133 | Link to this section 134 | 135 | Functions 136 |

137 |
138 | 139 |
140 | 141 | 142 | Link to this function 143 | 144 | query(bucket, query) 145 | 146 | 147 | 148 | View Source 149 | 150 | 151 | 152 | 153 |
154 |
155 | 156 |
157 |
158 |
159 | 160 |
161 | 162 | 163 | Link to this function 164 | 165 | query(pid, bucket, query) 166 | 167 | 168 | 169 | View Source 170 | 171 | 172 | 173 | 174 |
175 |
176 |

@deprecated There are performance issues with MapReduce, please use another query strategy. 177 | Perform a MapReduce job across the cluster using an entire bucket as the input. 178 | See the MapReduce documentation for explanation of behavior.

179 | 180 |
181 |
182 |
183 | 184 |
185 | 186 | 187 | Link to this function 188 | 189 | query(pid, bucket, query, timeout) 190 | 191 | 192 | 193 | View Source 194 | 195 | 196 | 197 | 198 |
199 |
200 | 201 |
202 |
203 | 204 |
205 | 206 | 207 | 208 | 221 |
222 |
223 |
224 |
225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /doc/Riak.Mapred.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Mapred – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Mapred 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |
100 |
101 | 104 | 105 |

@deprecated There are performance issues with MapReduce, please use another query strategy. 106 | Perform a MapReduce job across the cluster. 107 | See the MapReduce documentation for explanation of behavior

108 |
109 | 110 |
111 |
112 | 115 | 116 |
117 | 118 |
119 | 120 | 121 | 122 | 123 |
124 | 125 | 126 | 127 | 128 | 129 |
130 |

131 | 132 | 133 | Link to this section 134 | 135 | Functions 136 |

137 |
138 | 139 |
140 | 141 | 142 | Link to this function 143 | 144 | query(inputs, query) 145 | 146 | 147 | 148 | View Source 149 | 150 | 151 | 152 | 153 |
154 |
155 | 156 |
157 |
158 |
159 | 160 |
161 | 162 | 163 | Link to this function 164 | 165 | query(pid, inputs, query) 166 | 167 | 168 | 169 | View Source 170 | 171 | 172 | 173 | 174 |
175 |
176 |

@deprecated There are performance issues with MapReduce, please use another query strategy. 177 | Perform a MapReduce job across the cluster. 178 | See the MapReduce documentation for explanation of behavior.

179 | 180 |
181 |
182 |
183 | 184 |
185 | 186 | 187 | Link to this function 188 | 189 | query(pid, inputs, query, timeout) 190 | 191 | 192 | 193 | View Source 194 | 195 | 196 | 197 | 198 |
199 |
200 | 201 |
202 |
203 | 204 |
205 | 206 | 207 | 208 | 221 |
222 |
223 |
224 |
225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /doc/Riak.Pool.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Pool – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Pool 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 |
78 |

[EXPERIMENTAL] 79 | This module adds defpool to define functions with a 80 | lower arity for each function so:

81 |

Riak.put(pid, bucket, key, data) -> 82 | Riak.put(bucket, key, data) that calls the previous function 83 | with a pid from the pool

84 | 85 |
86 | 87 | 88 | 89 |
90 |

91 | 92 | 93 | Link to this section 94 | 95 | Summary 96 |

97 | 98 | 99 | 100 |
101 |

102 | Functions 103 |

104 |
105 |
106 | defpool(args, list) 107 |
108 | 109 |
110 |
111 | 114 | 115 |
116 | 117 |
118 | 119 | 120 | 121 | 122 |
123 | 124 | 125 | 126 | 127 | 128 |
129 |

130 | 131 | 132 | Link to this section 133 | 134 | Functions 135 |

136 |
137 | 138 |
139 | 140 | 141 | Link to this macro 142 | 143 | defpool(args, list) 144 | 145 | 146 | 147 | View Source 148 | 149 | 150 | 151 | (macro) 152 | 153 | 154 |
155 |
156 | 157 |
158 |
159 |
160 | 161 | 162 | 163 |
164 | 165 | 166 | Link to this function 167 | 168 | take_group_member(group_name, timeout \\ 100) 169 | 170 | 171 | 172 | View Source 173 | 174 | 175 | 176 | 177 |
178 |
179 | 180 |
181 |
182 | 183 |
184 | 185 | 186 | 187 | 200 |
201 |
202 |
203 |
204 | 205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /doc/Riak.Search.Schema.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Search.Schema – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Search.Schema 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |
100 |
101 | 104 | 105 |
106 |
107 |
108 | get(schema) 109 |
110 | 111 |
112 |
113 |
114 | get(pid, schema) 115 |
116 | 117 |
118 | 119 |
120 | 121 | 122 | 123 | 124 |
125 | 126 | 127 | 128 | 129 | 130 |
131 |

132 | 133 | 134 | Link to this section 135 | 136 | Functions 137 |

138 |
139 | 140 |
141 | 142 | 143 | Link to this function 144 | 145 | create(schema, content) 146 | 147 | 148 | 149 | View Source 150 | 151 | 152 | 153 | 154 |
155 |
156 | 157 |
158 |
159 |
160 | 161 |
162 | 163 | 164 | Link to this function 165 | 166 | create(pid, schema, content) 167 | 168 | 169 | 170 | View Source 171 | 172 | 173 | 174 | 175 |
176 |
177 | 178 |
179 |
180 |
181 | 182 |
183 | 184 | 185 | Link to this function 186 | 187 | get(schema) 188 | 189 | 190 | 191 | View Source 192 | 193 | 194 | 195 | 196 |
197 |
198 | 199 |
200 |
201 |
202 | 203 |
204 | 205 | 206 | Link to this function 207 | 208 | get(pid, schema) 209 | 210 | 211 | 212 | View Source 213 | 214 | 215 | 216 | 217 |
218 |
219 | 220 |
221 |
222 | 223 |
224 | 225 | 226 | 227 | 240 |
241 |
242 |
243 |
244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /doc/Riak.Search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Riak.Search – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | Riak.Search 68 | 69 | 70 | 71 | View Source 72 | 73 | 74 |

75 | 76 | 77 | 78 | 79 |
80 |

81 | 82 | 83 | Link to this section 84 | 85 | Summary 86 |

87 | 88 | 89 | 90 |
91 |

92 | Functions 93 |

94 |
95 | 98 | 99 |
100 |
101 | 104 | 105 |
106 |
107 | 110 | 111 |
112 |
113 | 116 | 117 |
118 | 119 |
120 | 121 | 122 | 123 | 124 |
125 | 126 | 127 | 128 | 129 | 130 |
131 |

132 | 133 | 134 | Link to this section 135 | 136 | Functions 137 |

138 |
139 | 140 |
141 | 142 | 143 | Link to this function 144 | 145 | query(index, query) 146 | 147 | 148 | 149 | View Source 150 | 151 | 152 | 153 | 154 |
155 |
156 | 157 |
158 |
159 |
160 | 161 |
162 | 163 | 164 | Link to this function 165 | 166 | query(pid, index, query) 167 | 168 | 169 | 170 | View Source 171 | 172 | 173 | 174 | 175 |
176 |
177 | 178 |
179 |
180 |
181 | 182 |
183 | 184 | 185 | Link to this function 186 | 187 | query(pid, index, query, options) 188 | 189 | 190 | 191 | View Source 192 | 193 | 194 | 195 | 196 |
197 |
198 | 199 |
200 |
201 |
202 | 203 |
204 | 205 | 206 | Link to this function 207 | 208 | query(pid, index, query, options, timeout) 209 | 210 | 211 | 212 | View Source 213 | 214 | 215 | 216 | 217 |
218 |
219 | 220 |
221 |
222 | 223 |
224 | 225 | 226 | 227 | 240 |
241 |
242 |
243 |
244 | 245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /doc/api-reference.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | API Reference – Riak Elixir Client v1.1.6 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 23 | 59 | 60 |
61 |
62 |
63 | 64 | 65 |

66 | Riak Elixir Client v1.1.6 67 | API Reference 68 |

69 | 70 | 71 |
72 |

73 | 74 | Modules 75 |

76 | 77 |
78 |
79 | 80 | 81 |

Riak Elixir Client

82 |

Build Status

83 |
84 | 85 |
86 |
87 | 88 | 89 |
90 |
91 | 92 | 93 |
94 |
95 | 96 | 97 |

Common CRDT module

98 |
99 | 100 |
101 |
102 | 103 | 104 |

Counter data-type on Riak 2.0

105 |
106 | 107 |
108 |
109 | 110 | 111 |

Encapsulates a boolean datatype inside a CRDT.Map

112 |
113 | 114 |
115 |
116 | 117 | 118 |

Encapsulates Riak HLL

119 |
120 | 121 |
122 |
123 | 124 | 125 |

Encapsulates Riak maps

126 |
127 | 128 |
129 |
130 | 131 | 132 |

Encapsulates a binary data to be used on CRDT.Map’s

133 |
134 | 135 |
136 |
137 | 138 | 139 |

Encapsulates riakc_set using elixir Set functions as interface. 140 | It accepts only binary as items

141 |
142 | 143 |
144 |
145 | 146 | 147 |
148 |
149 | 150 | 151 |
152 |
153 | 154 | 155 |
156 |
157 | 158 | 159 |
160 |
161 | 162 | 163 |

The Data wrapper makes it convenient to work with Riak data in Elixir

164 |
165 | 166 |
167 |
168 | 169 | 170 |

[EXPERIMENTAL] 171 | This module adds defpool to define functions with a 172 | lower arity for each function so

173 |
174 | 175 |
176 |
177 | 178 | 179 |
180 |
181 | 182 | 183 |
184 |
185 | 186 | 187 |
188 |
189 | 190 | 191 |
192 | 193 |
194 |
195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 216 |
217 |
218 |
219 |
220 | 221 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /doc/dist/sidebar_items-8bfd31e79d.js: -------------------------------------------------------------------------------- 1 | sidebarNodes={"extras":[{"id":"api-reference","title":"API Reference","group":"","headers":[{"id":"Modules","anchor":"modules"}]}],"exceptions":[],"modules":[{"id":"Riak","title":"Riak","functions":[{"id":"delete/1","anchor":"delete/1"},{"id":"delete/2","anchor":"delete/2"},{"id":"delete/3","anchor":"delete/3"},{"id":"delete/4","anchor":"delete/4"},{"id":"find/2","anchor":"find/2"},{"id":"find/3","anchor":"find/3"},{"id":"find/4","anchor":"find/4"},{"id":"ping/0","anchor":"ping/0"},{"id":"ping/1","anchor":"ping/1"},{"id":"put/1","anchor":"put/1"},{"id":"put/2","anchor":"put/2"},{"id":"resolve/3","anchor":"resolve/3"},{"id":"resolve/4","anchor":"resolve/4"},{"id":"update/4","anchor":"update/4"},{"id":"update/5","anchor":"update/5"}]},{"id":"Riak.Bucket","title":"Riak.Bucket","functions":[{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"keys/1","anchor":"keys/1"},{"id":"keys/2","anchor":"keys/2"},{"id":"keys/3","anchor":"keys/3"},{"id":"keys/4","anchor":"keys/4"},{"id":"keys!/1","anchor":"keys!/1"},{"id":"keys!/2","anchor":"keys!/2"},{"id":"keys!/3","anchor":"keys!/3"},{"id":"keys!/4","anchor":"keys!/4"},{"id":"list/0","anchor":"list/0"},{"id":"list/1","anchor":"list/1"},{"id":"list/2","anchor":"list/2"},{"id":"list!/0","anchor":"list!/0"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"possible_props/0","anchor":"possible_props/0"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"put/4","anchor":"put/4"},{"id":"reset/1","anchor":"reset/1"},{"id":"reset/2","anchor":"reset/2"}]},{"id":"Riak.Bucket.Type","title":"Riak.Bucket.Type","functions":[{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"list/1","anchor":"list/1"},{"id":"list/2","anchor":"list/2"},{"id":"list/3","anchor":"list/3"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"list!/3","anchor":"list!/3"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"}]},{"id":"Riak.CRDT","title":"Riak.CRDT","functions":[{"id":"type/1","anchor":"type/1"}]},{"id":"Riak.CRDT.Counter","title":"Riak.CRDT.Counter","functions":[{"id":"decrement/2","anchor":"decrement/2"},{"id":"increment/2","anchor":"increment/2"},{"id":"new/0","anchor":"new/0"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Flag","title":"Riak.CRDT.Flag","functions":[{"id":"disable/1","anchor":"disable/1"},{"id":"enable/1","anchor":"enable/1"},{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"new/2","anchor":"new/2"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.HyperLogLog","title":"Riak.CRDT.HyperLogLog","functions":[{"id":"add_element/2","anchor":"add_element/2"},{"id":"add_elements/2","anchor":"add_elements/2"},{"id":"new/0","anchor":"new/0"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Map","title":"Riak.CRDT.Map","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"get/3","anchor":"get/3"},{"id":"has_key?/2","anchor":"has_key?/2"},{"id":"keys/1","anchor":"keys/1"},{"id":"new/0","anchor":"new/0"},{"id":"put/3","anchor":"put/3"},{"id":"size/1","anchor":"size/1"},{"id":"update/4","anchor":"update/4"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Register","title":"Riak.CRDT.Register","functions":[{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"set/2","anchor":"set/2"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.CRDT.Set","title":"Riak.CRDT.Set","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"member?/2","anchor":"member?/2"},{"id":"new/0","anchor":"new/0"},{"id":"new/1","anchor":"new/1"},{"id":"new/2","anchor":"new/2"},{"id":"put/2","anchor":"put/2"},{"id":"size/1","anchor":"size/1"},{"id":"value/1","anchor":"value/1"}]},{"id":"Riak.Connection","title":"Riak.Connection","functions":[{"id":"start/3","anchor":"start/3"},{"id":"start_link/3","anchor":"start_link/3"}]},{"id":"Riak.Index","title":"Riak.Index","functions":[{"id":"query/4","anchor":"query/4"},{"id":"query/5","anchor":"query/5"},{"id":"query/6","anchor":"query/6"}]},{"id":"Riak.Mapred","title":"Riak.Mapred","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"}]},{"id":"Riak.Mapred.Bucket","title":"Riak.Mapred.Bucket","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"}]},{"id":"Riak.Object","title":"Riak.Object","functions":[{"id":"__struct__/0","anchor":"__struct__/0"},{"id":"create/1","anchor":"create/1"},{"id":"delete_all_indexes/1","anchor":"delete_all_indexes/1"},{"id":"delete_all_links/1","anchor":"delete_all_links/1"},{"id":"delete_all_metadata/1","anchor":"delete_all_metadata/1"},{"id":"delete_index/2","anchor":"delete_index/2"},{"id":"delete_link/2","anchor":"delete_link/2"},{"id":"delete_metadata/2","anchor":"delete_metadata/2"},{"id":"from_robj/1","anchor":"from_robj/1"},{"id":"get_all_indexes/1","anchor":"get_all_indexes/1"},{"id":"get_all_links/1","anchor":"get_all_links/1"},{"id":"get_all_metadata/1","anchor":"get_all_metadata/1"},{"id":"get_index/2","anchor":"get_index/2"},{"id":"get_link/2","anchor":"get_link/2"},{"id":"get_metadata/2","anchor":"get_metadata/2"},{"id":"index_id/1","anchor":"index_id/1"},{"id":"put_index/3","anchor":"put_index/3"},{"id":"put_link/4","anchor":"put_link/4"},{"id":"put_metadata/2","anchor":"put_metadata/2"},{"id":"to_robj/1","anchor":"to_robj/1"}]},{"id":"Riak.Pool","title":"Riak.Pool","functions":[{"id":"defpool/2","anchor":"defpool/2"},{"id":"take_group_member/2","anchor":"take_group_member/2"}]},{"id":"Riak.Search","title":"Riak.Search","functions":[{"id":"query/2","anchor":"query/2"},{"id":"query/3","anchor":"query/3"},{"id":"query/4","anchor":"query/4"},{"id":"query/5","anchor":"query/5"}]},{"id":"Riak.Search.Index","title":"Riak.Search.Index","functions":[{"id":"delete/1","anchor":"delete/1"},{"id":"delete/2","anchor":"delete/2"},{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"},{"id":"list/0","anchor":"list/0"},{"id":"list/1","anchor":"list/1"},{"id":"put/1","anchor":"put/1"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"put/4","anchor":"put/4"}]},{"id":"Riak.Search.Schema","title":"Riak.Search.Schema","functions":[{"id":"create/2","anchor":"create/2"},{"id":"create/3","anchor":"create/3"},{"id":"get/1","anchor":"get/1"},{"id":"get/2","anchor":"get/2"}]},{"id":"Riak.Timeseries","title":"Riak.Timeseries","functions":[{"id":"delete/2","anchor":"delete/2"},{"id":"delete/3","anchor":"delete/3"},{"id":"delete/4","anchor":"delete/4"},{"id":"get/2","anchor":"get/2"},{"id":"get/3","anchor":"get/3"},{"id":"get/4","anchor":"get/4"},{"id":"list!/1","anchor":"list!/1"},{"id":"list!/2","anchor":"list!/2"},{"id":"list!/3","anchor":"list!/3"},{"id":"put/2","anchor":"put/2"},{"id":"put/3","anchor":"put/3"},{"id":"query/1","anchor":"query/1"},{"id":"query/2","anchor":"query/2"}]}],"protocols":[],"tasks":[]} -------------------------------------------------------------------------------- /doc/fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.eot -------------------------------------------------------------------------------- /doc/fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /doc/fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.ttf -------------------------------------------------------------------------------- /doc/fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/doc/fonts/icomoon.woff -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Riak Elixir Client v1.1.6 – Documentation 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/exploriak/.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | erl_crash.dump 4 | *.ez 5 | -------------------------------------------------------------------------------- /examples/exploriak/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: 4 | rm -f exploriak 5 | mix deps.get 6 | mix escript.build -------------------------------------------------------------------------------- /examples/exploriak/README.md: -------------------------------------------------------------------------------- 1 | Exploriak 2 | ============ 3 | 4 | A simple Elixir CLI app to perform a few actions against a local Riak node. 5 | 6 | ## Build 7 | 8 | ``` 9 | make 10 | ``` 11 | 12 | ## Usage 13 | 14 | Output of `./exploriak --help`: 15 | 16 | ``` 17 | Usage: 18 | ./exploriak [operation] [options] 19 | Options: 20 | --help # Show this help message and quit. 21 | Listing: 22 | --list # (boolean) List keys or buckets 23 | --keys # (boolean) If not used, only buckets will be listed 24 | Selectors: 25 | --type # (string) Bucket type 26 | --bucket # (string) Bucket 27 | 28 | Examples: 29 | ./exploriak --list # List all buckets in default bucket type 30 | ./exploriak --list --type mytype # List all buckets in mytype bucket type 31 | ./exploriak --list --keys --bucket mybucket # List all keys in default bucket type, mybucket 32 | ``` -------------------------------------------------------------------------------- /examples/exploriak/exploriak: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenRiak/riak-elixir-client/c58700ec30a451a46b226a09387144becc47d5ae/examples/exploriak/exploriak -------------------------------------------------------------------------------- /examples/exploriak/lib/exploriak.ex: -------------------------------------------------------------------------------- 1 | defmodule Exploriak do 2 | 3 | def main(args) do 4 | args |> parse_args |> process 5 | end 6 | 7 | def parse_args(args) do 8 | switches = 9 | [ 10 | help: :boolean, 11 | ## List 12 | list: :boolean, 13 | keys: :boolean, 14 | type: :string, 15 | bucket: :string 16 | ] 17 | 18 | options = OptionParser.parse(args, switches: switches) 19 | 20 | case options do 21 | {[help: true],_,_} -> :help 22 | ## List 23 | {[list: true],_,_} -> [:list_buckets] 24 | {[list: true, type: type],_,_} -> [:list_buckets, type] 25 | {[list: true, keys: true],_,_} -> [:list_keys] 26 | {[list: true, keys: true, bucket: bucket],_,_} -> [:list_keys, bucket] 27 | {[list: true, keys: true, type: type, bucket: bucket],_,_} -> [:list_keys, type, bucket] 28 | _ -> :help 29 | end 30 | end 31 | 32 | def process(:help) do 33 | IO.puts """ 34 | Usage: 35 | ./exploriak [operation] [options] 36 | Options: 37 | --help # Show this help message and quit. 38 | Listing: 39 | --list # (boolean) List keys or buckets 40 | --keys # (boolean) If not used, only buckets will be listed 41 | Selectors: 42 | --type # (string) Bucket type 43 | --bucket # (string) Bucket 44 | 45 | Examples: 46 | ./exploriak --list # List all buckets in default bucket type 47 | ./exploriak --list --type mytype # List all buckets in mytype bucket type 48 | ./exploriak --list --keys --bucket mybucket # List all keys in default bucket type, mybucket 49 | 50 | """ 51 | System.halt(0) 52 | end 53 | 54 | ## List 55 | def process([:list_buckets]) do 56 | IO.puts "Listing all buckets in default type" 57 | for bucket <- Riak.Bucket.list!(get_pid) do 58 | IO.puts ["Bucket: ", bucket] 59 | end 60 | end 61 | 62 | def process([:list_buckets, type]) do 63 | IO.puts "Listing all buckets in default type" 64 | for bucket <- Riak.Bucket.Type.list!(get_pid, type) do 65 | IO.puts ["Bucket: ", bucket] 66 | end 67 | end 68 | 69 | def process([:list_keys, bucket]) do 70 | IO.puts ["Listing all keys in default type, ", bucket, " bucket"] 71 | for key <- Riak.Bucket.keys!(get_pid, bucket) do 72 | IO.puts ["Bucket: ", bucket, ", Key: ", key] 73 | end 74 | end 75 | 76 | def process([:list_keys, type, bucket]) do 77 | IO.puts ["Listing all keys in ", type, " type, ", bucket, " bucket"] 78 | for key <- Riak.Bucket.Type.keys!(get_pid, type, bucket) do 79 | IO.puts ["Bucket: ", bucket, ", Key: ", key] 80 | end 81 | end 82 | 83 | def process([:list_keys]) do 84 | IO.puts ["Listing all keys in default type, all buckets"] 85 | for bucket <- Riak.Bucket.list!(get_pid), key <- Riak.Bucket.keys!(get_pid, bucket) do 86 | IO.puts ["Bucket: ", bucket, ", Key: ", key] 87 | end 88 | end 89 | 90 | defp get_pid do 91 | {:ok, pid } = Riak.Connection.start('127.0.0.1', 8087) 92 | pid 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /examples/exploriak/mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Exploriak.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ app: :exploriak, 6 | version: "0.0.1", 7 | elixir: "~> 1.0", 8 | deps: deps, 9 | escript: escript ] 10 | end 11 | 12 | def escript do 13 | [ main_module: Exploriak, 14 | app: :riak ] 15 | end 16 | 17 | defp deps do 18 | [ {:riak, "~> 1.0"} ] 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /examples/exploriak/mix.lock: -------------------------------------------------------------------------------- 1 | %{"hamcrest": {:hex, :hamcrest, "0.1.1"}, 2 | "linguist": {:hex, :linguist, "0.1.5"}, 3 | "meck": {:hex, :meck, "0.8.3"}, 4 | "pooler": {:hex, :pooler, "1.5.0"}, 5 | "protobuffs": {:hex, :protobuffs, "0.8.2"}, 6 | "riak": {:hex, :riak, "1.0.0"}, 7 | "riak_pb": {:hex, :riak_pb, "2.1.0"}, 8 | "riakc": {:hex, :riakc, "2.1.1"}} 9 | -------------------------------------------------------------------------------- /examples/exploriak/test/exploriak_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ExploriakTest do 2 | use ExUnit.Case 3 | 4 | test "the truth" do 5 | assert 1 + 1 == 2 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /examples/exploriak/test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | -------------------------------------------------------------------------------- /lib/riak.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak do 2 | @moduledoc """ 3 | # Riak Elixir Client 4 | [![Build Status](https://travis-ci.org/drewkerrigan/riak-elixir-client.svg?branch=master)](https://travis-ci.org/drewkerrigan/riak-elixir-client) 5 | 6 | A Riak client written in Elixir. Now includes connection pooling with [pooler](http://github.com/seth/pooler) and a variety of other improvements from [riex](https://github.com/edgurgel/riex). 7 | 8 | ## Setup 9 | 10 | ### Prerequisites 11 | 12 | * Riak 2.0+ 13 | * Elixir 1.0+ 14 | 15 | #### In an Elixir application 16 | 17 | Add the following to mix.exs 18 | 19 | ```elixir 20 | ... 21 | def application do 22 | [ applications: [ :riak ]] 23 | end 24 | ... 25 | defp deps do 26 | [ {:riak, "~> 1.0"} ] 27 | end 28 | ... 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### Establishing a Riak connection 34 | 35 | ```elixir 36 | {:ok, pid} = Riak.Connection.start_link('127.0.0.1', 8087) # Default values 37 | ``` 38 | 39 | ### Connection Pooling 40 | 41 | Most functions in this module can be called by passing the pid of the established connection or using a pool of connections (provided by pooler). Define pools by using the group `riak`. Following is an example `config/config.exs`: 42 | 43 | ```elixir 44 | [pooler: [pools: [ 45 | [ name: :riaklocal1, 46 | group: :riak, 47 | max_count: 10, 48 | init_count: 5, 49 | start_mfa: {Riak.Connection, :start_link, []} 50 | ], 51 | [ name: :riaklocal2, 52 | group: :riak, 53 | max_count: 15, 54 | init_count: 2, 55 | start_mfa: {Riak.Connection, :start_link, ['127.0.0.1', 9090]} 56 | ] ] 57 | ]] 58 | 59 | ``` 60 | 61 | For an example using this functionality with a local Riak instance, check [`config/config.exs`](https://github.com/drewkerrigan/riak-elixir-client/blob/master/config/config.exs). More information about Elixir configuration can be found on [http://elixir-lang.org(http://elixir-lang.org)]: [Application environment and configuration](http://elixir-lang.org/getting_started/mix_otp/10.html#toc_6). 62 | 63 | Once a pool configuration is properly defined in a project, calls to Riak can omit the pid. For example: 64 | 65 | This call uses a pid from the pool of connections provided by pooler: 66 | 67 | ```elixir 68 | Riak.delete("user", key) 69 | ``` 70 | 71 | This call requires a pid obtained by first calling `Riak.Connection.start_link`: 72 | 73 | ```elixir 74 | Riak.delete(pid, "user", key) 75 | ``` 76 | 77 | ### Save a value 78 | 79 | ```elixir 80 | o = Riak.Object.create(bucket: "user", key: "my_key", data: "Han Solo") 81 | Riak.put(pid, o) 82 | ``` 83 | 84 | ### Find an object 85 | 86 | ```elixir 87 | o = Riak.find(pid, "user", "my_key") 88 | ``` 89 | 90 | ### Update an object 91 | 92 | ```elixir 93 | o = %{o | data: "Something Else"} 94 | Riak.put(pid, o) 95 | ``` 96 | 97 | ### Delete an object 98 | 99 | Using key 100 | 101 | ```elixir 102 | Riak.delete(pid, "user", key) 103 | ``` 104 | 105 | Using object 106 | 107 | ```elixir 108 | Riak.delete(pid, o) 109 | ``` 110 | 111 | ### Datatypes 112 | 113 | Riak Datatypes (a.k.a. CRDTs) are avaiable in [Riak versions 2.0](http://basho.com/introducing-riak-2-0/) and greater. The types included are: maps, sets, counters, registers and flags. 114 | 115 | #### Setup 116 | 117 | Datatypes require the use of bucket-types. Maps, sets, and counters can be used as top-level bucket-type datatypes; Registers and flags may only be used within maps. 118 | 119 | The following examples assume the presence of 3 datatype enabled bucket-types. You can create these bucket-types by running the following commands on a single Riak node in your cluster: 120 | 121 | Bucket-Type: `counters` 122 | 123 | ``` 124 | riak-admin bucket-type create counters '{"props":{"datatype":"counter"}}' 125 | riak-admin bucket-type activate counters 126 | ``` 127 | 128 | Bucket-Type: `sets` 129 | 130 | ``` 131 | riak-admin bucket-type create sets '{"props":{"datatype":"set"}}' 132 | riak-admin bucket-type activate sets 133 | ``` 134 | 135 | Bucket-Type: `maps` 136 | 137 | ``` 138 | riak-admin bucket-type create maps '{"props":{"datatype":"map"}}' 139 | riak-admin bucket-type activate maps 140 | ``` 141 | 142 | #### Counters 143 | 144 | Create a counter (`alias Riak.CRDT.Counter`): 145 | 146 | ```elixir 147 | Counter.new 148 | |> Counter.increment 149 | |> Counter.increment(2) 150 | |> Riak.update("counters", "my_counter_bucket", "my_key") 151 | ``` 152 | 153 | Fetch a counter: 154 | 155 | ```elixir 156 | counter = Riak.find("counters", "my_counter_bucket", "my_key") 157 | |> Counter.value 158 | ``` 159 | 160 | `counter` will be 3. 161 | 162 | ***NOTE***: "Counter drift" is a possibility that needs to be accounted for with any distributed system such as Riak. The problem can manifest itself during failure states in either your applicaiton or Riak itself. If an increment operation fails from the client's point of view, there is not sufficient information available to know whether or not that call made it to zero or all of the replicas for that counter object. As such, if the client attempts to retry the increment after recieving something like a error code 500 from Riak, that counter object is at risk of drifting positive. Similarly if the client decides not to retry, that counter object is at risk of drifting negative. 163 | 164 | For these reasons, counters are only suggested for use-cases that can handle some (albeit small) amount of counter drift. Good examples of appropriate use-cases are: Facebook likes, Twitter retweet counts, Youtube view counts, etc. Some examples of poor use-cases for Riak counters are: bank account balances, anything related to money. It is possible to implement these types of solutions using Riak, but more client side logic is necessary. For an example of a client-side ledger with tunable retry options, check [github.com/drewkerrigan/riak-ruby-ledger](https://github.com/drewkerrigan/riak-ruby-ledger). Another approach could be the client-side implementation of a HAT (Highly Available Transaction) algorithm. 165 | 166 | #### Sets 167 | 168 | Create a set (`alias Riak.CRDT.Set`): 169 | 170 | ```elixir 171 | Set.new 172 | |> Set.put("foo") 173 | |> Set.put("bar") 174 | |> Riak.update("sets", "my_set_bucket", "my_key") 175 | ``` 176 | 177 | And fetch the set: 178 | 179 | ```elixir 180 | set = Riak.find("sets", "my_set_bucket", "my_key") 181 | |> Set.value 182 | ``` 183 | 184 | Where `set` is an `orddict`. 185 | 186 | #### Maps 187 | 188 | Maps handle binary keys with any other datatype (map, set, flag, register and counter). 189 | 190 | Create a map (`alias Riak.CRDT.Map`): 191 | 192 | ```elixir 193 | register = Register.new("some string") 194 | flag = Flag.new |> Flag.enable 195 | Map.new 196 | |> Map.put("k1", register) 197 | |> Map.put("k2", flag) 198 | |> Riak.update("maps", "my_map_bucket", "map_key") 199 | ``` 200 | 201 | And fetch the map: 202 | 203 | ```elixir 204 | map = Riak.find("maps", "my_map_bucket", key) |> Map.value 205 | ``` 206 | 207 | Where `map` is an `orddict`. 208 | 209 | ## Examples 210 | 211 | Check the `examples/` directory for a few example elixir applications using the riak client. 212 | 213 | For more functionality, check `test/` directory. 214 | 215 | ## Tests 216 | 217 | ``` 218 | MIX_ENV=test mix do deps.get, test 219 | ``` 220 | 221 | ## License 222 | 223 | Copyright 2015 Drew Kerrigan. 224 | Copyright 2014 Eduardo Gurgel. 225 | 226 | Licensed under the Apache License, Version 2.0 (the "License"); 227 | you may not use this file except in compliance with the License. 228 | You may obtain a copy of the License at 229 | 230 | http://www.apache.org/licenses/LICENSE-2.0 231 | 232 | Unless required by applicable law or agreed to in writing, software 233 | distributed under the License is distributed on an "AS IS" BASIS, 234 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 235 | See the License for the specific language governing permissions and 236 | limitations under the License. 237 | 238 | """ 239 | import Riak.Pool 240 | require Record 241 | 242 | @doc """ 243 | Ping the server. 244 | """ 245 | defpool ping(pid) when is_pid(pid), do: :riakc_pb_socket.ping(pid) 246 | 247 | @doc """ 248 | Put the metadata/value in the object under bucket-type/bucket/key. 249 | """ 250 | defpool put(pid, obj) when is_pid(pid) do 251 | case :riakc_pb_socket.put(pid, Riak.Object.to_robj(obj)) do 252 | {:ok, new_key} when is_binary(new_key) -> %{obj | key: new_key} 253 | {:ok, new_object} -> %{obj | key: :riakc_obj.key(new_object)} 254 | :ok -> obj 255 | {:error, :notfound} -> nil 256 | {:error, term} -> {:error, term} 257 | end 258 | end 259 | 260 | @doc """ 261 | Updates the convergent datatype in Riak with local 262 | modifications stored in the container type. 263 | """ 264 | defpool update(pid, datatype, type, bucket, key) when is_pid(pid) do 265 | :riakc_pb_socket.update_type(pid, {type, bucket}, 266 | key, to_op(datatype)) 267 | end 268 | 269 | defp to_op(datatype) do 270 | case datatype do 271 | datatype when Record.is_record(datatype, :set) -> 272 | :riakc_set.to_op(datatype) 273 | datatype when Record.is_record(datatype, :counter) -> 274 | :riakc_counter.to_op(datatype) 275 | datatype when Record.is_record(datatype, :map) -> 276 | :riakc_map.to_op(datatype) 277 | datatype when Record.is_record(datatype, :hll) -> 278 | :riakc_hll.to_op(datatype) 279 | _ -> :undefined 280 | end 281 | end 282 | 283 | @doc """ 284 | Get bucket-type/bucket/key from the server. 285 | """ 286 | defpool find(pid, bucket, key) when is_pid(pid) do 287 | case :riakc_pb_socket.get(pid, bucket, key) do 288 | {:ok, object} -> 289 | if :riakc_obj.value_count(object) > 1 do 290 | build_sibling_list(:riakc_obj.get_contents(object),[]) 291 | else 292 | Riak.Object.from_robj(object) 293 | end 294 | {:error, :notfound} -> nil 295 | {:error, term} -> {:error, term} 296 | end 297 | end 298 | 299 | @doc """ 300 | Fetches the representation of a convergent datatype from Riak. 301 | 302 | TODO: In the current implementation, it's very easy to confuse working 303 | with regular k/v objects and datatypes. Clarify so that these aren't 304 | conflated by assuming that any object with a type is a datatype. 305 | """ 306 | defpool find(pid, type, bucket, key) when is_pid(pid) do 307 | case :riakc_pb_socket.fetch_type(pid, {type, bucket}, key) do 308 | {:ok, object} -> object 309 | {:error, :notfound} -> nil 310 | {:error, {:notfound, _crdt}} -> nil 311 | {:error, term} -> {:error, term} 312 | end 313 | end 314 | 315 | # [["X-Riak-Deleted" | true]] 316 | defp sibling_deleted?(md) do 317 | presented = md 318 | |> :dict.fetch_keys 319 | |> Enum.find(& &1 == "X-Riak-Deleted") 320 | presented && :dict.fetch("X-Riak-Deleted", md) 321 | end 322 | defp build_sibling_list([], [final_data] = _final_list), do: %Riak.Object{data: final_data} 323 | defp build_sibling_list([], final_list), do: final_list 324 | defp build_sibling_list([{md, val}|t], final_list) do 325 | build_sibling_list(t, (if sibling_deleted?(md), do: final_list, else: [val|final_list])) 326 | end 327 | 328 | @doc """ 329 | Picks the sibling to "win" over the other siblings via a list index. 330 | """ 331 | defpool resolve(pid, bucket, key, index) when is_pid(pid) do 332 | case :riakc_pb_socket.get(pid, bucket, key) do 333 | {:ok, object} -> 334 | new_object = :riakc_obj.select_sibling(index, object) 335 | :riakc_pb_socket.put(pid, new_object) 336 | {:error, term} -> {:error, term} 337 | end 338 | end 339 | 340 | @doc """ 341 | Delete the key/value. 342 | """ 343 | defpool delete(pid, obj) when is_pid(pid), do: delete(pid, obj.bucket, obj.key) 344 | defpool delete(pid, bucket, key) when is_pid(pid), do: :riakc_pb_socket.delete(pid, bucket, key) 345 | defpool delete(pid, type, bucket, key) when is_pid(pid), do: :riakc_pb_socket.delete(pid, {type, bucket}, key) 346 | end 347 | -------------------------------------------------------------------------------- /lib/riak/bucket.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Bucket do 2 | import Riak.Pool 3 | import :riakc_pb_socket, except: [put: 3, put: 4] 4 | 5 | @doc """ 6 | List all buckets on the server with or without server-side timeout. 7 | ***This is a potentially expensive operation and should not be used in production.*** 8 | """ 9 | defpool list(pid) when is_pid(pid), do: list_buckets(pid) 10 | defpool list(pid, timeout) when is_pid(pid), do: list_buckets(pid, timeout) 11 | 12 | @doc """ 13 | List all buckets on the server with or without server-side timeout. Return only buckets. 14 | ***This is a potentially expensive operation and should not be used in production.*** 15 | """ 16 | defpool list!(pid) when is_pid(pid) do 17 | {:ok, buckets} = list(pid) 18 | buckets 19 | end 20 | defpool list!(pid, timeout) when is_pid(pid) do 21 | {:ok, buckets} = list(pid, timeout) 22 | buckets 23 | end 24 | 25 | @doc """ 26 | List all keys in a bucket. 27 | ***This is a potentially expensive operation and should not be used in production.*** 28 | """ 29 | def keys(pid, type, bucket) when is_pid(pid) and is_binary(bucket), do: keys(pid, {type, bucket}) 30 | def keys(pid, type, bucket, timeout) when is_pid(pid), do: keys(pid, {type, bucket}, timeout) 31 | defpool keys(pid, bucket) when is_pid(pid), do: list_keys(pid, bucket) 32 | defpool keys(pid, bucket, timeout) when is_pid(pid), do: list_keys(pid, bucket, timeout) 33 | 34 | @doc """ 35 | List all keys in a bucket. Return only buckets. 36 | ***This is a potentially expensive operation and should not be used in production.*** 37 | """ 38 | def keys!(pid, type, bucket) when is_pid(pid) and is_binary(bucket), do: keys!(pid, {type, bucket}) 39 | def keys!(pid, type, bucket, timeout) when is_pid(pid), do: keys!(pid, {type, bucket}, timeout) 40 | defpool keys!(pid, bucket) when is_pid(pid) do 41 | {:ok, keys} = keys(pid, bucket) 42 | keys 43 | end 44 | defpool keys!(pid, bucket, timeout) when is_pid(pid) do 45 | {:ok, keys} = keys(pid, bucket, timeout) 46 | keys 47 | end 48 | 49 | @doc """ 50 | Get bucket properties. 51 | """ 52 | defpool get(pid, bucket) when is_pid(pid), do: get_bucket(pid, bucket) 53 | 54 | @doc """ 55 | Set bucket properties. 56 | """ 57 | defpool put(pid, bucket, props) when is_pid(pid), do: set_bucket(pid, bucket, props) 58 | defpool put(pid, bucket, type, props) when is_pid(pid) do 59 | set_bucket(pid, {type, bucket}, props) 60 | end 61 | 62 | @doc """ 63 | Reset bucket properties back to the defaults. 64 | """ 65 | defpool reset(pid, bucket) when is_pid(pid), do: reset_bucket(pid, bucket) 66 | 67 | defmodule Type do 68 | @doc """ 69 | Get properties for bucket type. 70 | """ 71 | defpool get(pid, type) when is_pid(pid), do: get_bucket_type(pid, type) 72 | 73 | @doc """ 74 | Set properties for bucket type. 75 | """ 76 | defpool put(pid, type, props) when is_pid(pid), do: set_bucket_type(pid, type, props) 77 | 78 | defpool list(pid, type) when is_pid(pid) and is_binary(type), do: list_buckets(pid, type) 79 | defpool list(pid, type, timeout) when is_pid(pid), do: list_buckets(pid, type, timeout) 80 | 81 | defpool list!(pid, type) when is_pid(pid) and is_binary(type) do 82 | {:ok, buckets} = list(pid, type) 83 | buckets 84 | end 85 | defpool list!(pid, type, timeout) when is_pid(pid) do 86 | {:ok, buckets} = list(pid, type, timeout) 87 | buckets 88 | end 89 | end 90 | 91 | @doc """ 92 | Used to "register" the atoms used by the protobuf interface, not for public use. 93 | """ 94 | def possible_props do 95 | [n_val: 3, old_vclock: 86400, young_vclock: 20, big_vclock: 50, small_vclock: 50, allow_mult: false, last_write_wins: false, 96 | basic_quorum: false, notfound_ok: false, precommit: [], postcommit: [], chash_keyfun: {:riak_core_util, :chash_std_keyfun}, 97 | linkfun: {:modfun, :riak_kv_wm_link_walker, :mapreduce_linkfun}, pr: 0, r: :quorum, w: :quorum, pw: 0, dw: :quorum, 98 | rw: :quorum] 99 | end 100 | end 101 | 102 | -------------------------------------------------------------------------------- /lib/riak/connection.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Connection do 2 | @doc """ 3 | Create a linked process to talk with the riak server on host:port. 4 | """ 5 | def start_link(host \\ '127.0.0.1', port \\ 8087, args \\ []) do 6 | :riakc_pb_socket.start_link(host, port, args) 7 | end 8 | 9 | @doc """ 10 | Create a process to talk with the riak server on host:port. 11 | """ 12 | def start(host \\ '127.0.0.1', port \\ 8087, args \\ []) do 13 | :riakc_pb_socket.start(host, port, args) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/riak/crdt.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT do 2 | @moduledoc """ 3 | Common CRDT module 4 | """ 5 | require Record 6 | 7 | Enum.each [:set, :map, :counter, :register, :flag, :hll], fn t -> 8 | def type(value) when Record.is_record(value, unquote(t)), do: unquote(t) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/riak/crdt/counter.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.Counter do 2 | @moduledoc """ 3 | Counter data-type on Riak 2.0. 4 | """ 5 | require Record 6 | 7 | @doc """ 8 | Create a new counter 9 | """ 10 | def new(), do: :riakc_counter.new() 11 | 12 | @doc """ 13 | Increment a `counter` on the `amount` defaulting in 1 14 | """ 15 | def increment(counter, amount \\ 1) 16 | def increment(counter, amount) when Record.is_record(counter, :counter) do 17 | :riakc_counter.increment(amount, counter) 18 | end 19 | def increment(nil, _), do: {:error, :nil_object} 20 | def increment({:error, term}, _), do: {:error, term} 21 | 22 | @doc """ 23 | Decrement a `counter` on the `amount` defaulting in 1 24 | """ 25 | def decrement(counter, amount \\ 1) 26 | def decrement(counter, amount) when Record.is_record(counter, :counter) do 27 | :riakc_counter.increment(-amount, counter) 28 | end 29 | def decrement(nil, _), do: {:error, :nil_object} 30 | def decrement({:error, term}, _), do: {:error, term} 31 | 32 | @doc """ 33 | Get the original value as a number 34 | """ 35 | def value(counter) when Record.is_record(counter, :counter) do 36 | :riakc_counter.value(counter) 37 | end 38 | def value(nil), do: {:error, :nil_object} 39 | def value({:error, term}), do: {:error, term} 40 | end 41 | -------------------------------------------------------------------------------- /lib/riak/crdt/flag.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.Flag do 2 | @moduledoc """ 3 | Encapsulates a boolean datatype inside a CRDT.Map 4 | """ 5 | require Record 6 | 7 | @doc """ 8 | Creates a new flag container 9 | """ 10 | def new(), do: :riakc_flag.new() 11 | def new(context), do: :riakc_flag.new(context) 12 | def new(value, context) when is_boolean(value), do: :riakc_flag.new(value, context) 13 | 14 | @doc """ 15 | Extracts current value of `flag` 16 | """ 17 | def value(flag) when Record.is_record(flag, :flag) do 18 | :riakc_flag.value(flag) 19 | end 20 | def value(nil), do: {:error, :nil_object} 21 | def value({:error, term}), do: {:error, term} 22 | 23 | @doc """ 24 | Turns the value to true 25 | """ 26 | def enable(flag) when Record.is_record(flag, :flag) do 27 | :riakc_flag.enable(flag) 28 | end 29 | def enable(nil), do: {:error, :nil_object} 30 | def enable({:error, term}), do: {:error, term} 31 | 32 | @doc """ 33 | Turns the value to false 34 | """ 35 | def disable(flag) when Record.is_record(flag, :flag) do 36 | # This is not ideal, create a :riakc_flag::flag() record with a disable op 37 | case flag do 38 | {:flag, value, _, :undefined} -> 39 | {:flag, value, :disable, :undefined}; 40 | _ -> :riakc_flag.disable(flag) 41 | end 42 | end 43 | def disable(nil), do: {:error, :nil_object} 44 | def disable({:error, term}), do: {:error, term} 45 | end 46 | -------------------------------------------------------------------------------- /lib/riak/crdt/hll.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.HyperLogLog do 2 | @moduledoc """ 3 | Encapsulates Riak HLL 4 | """ 5 | require Record 6 | 7 | @doc """ 8 | Creates a new `hll` 9 | """ 10 | def new(), do: :riakc_hll.new() 11 | 12 | @doc """ 13 | Adds a distinct element to the HLL 14 | """ 15 | def add_element(hll, elm), do: :riakc_hll.add_element(elm, hll) 16 | 17 | @doc """ 18 | Adds a list of elements to the HLL 19 | """ 20 | def add_elements(nil, _), do: {:error, :nil_object} 21 | def add_elements({:error, term}, _), do: {:error, term} 22 | def add_elements(hll, list) when is_list(list), do: :riakc_hll.add_elements(list, hll) 23 | 24 | @doc """ 25 | Fetch the value of the HLL 26 | """ 27 | def value(nil), do: {:error, :nil_object} 28 | def value({:error, term}), do: {:error, term} 29 | def value(hll), do: :riakc_hll.value(hll) 30 | end 31 | -------------------------------------------------------------------------------- /lib/riak/crdt/map.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.Map do 2 | @moduledoc """ 3 | Encapsulates Riak maps 4 | """ 5 | require Record 6 | 7 | @doc """ 8 | Creates a new `map` 9 | """ 10 | def new(), do: :riakc_map.new() 11 | 12 | @doc """ 13 | Get the `map` size 14 | """ 15 | def size(map) when Record.is_record(map, :map), do: :riakc_map.size(map) 16 | def size(nil), do: {:error, :nil_object} 17 | def size({:error, term}), do: {:error, term} 18 | 19 | @doc """ 20 | Fetch the value associated to `key` with the `key_type` on `map` 21 | """ 22 | def get(map, key_type, key) when Record.is_record(map, :map) do 23 | :riakc_map.fetch({key, key_type}, map) 24 | end 25 | def get(nil, _, _), do: {:error, :nil_object} 26 | def get({:error, term}, _, _), do: {:error, term} 27 | 28 | @doc """ 29 | Update the `key` on the `map` by passing the function `fun` 30 | to update the value based on the current value (if exists) as argument 31 | The key_type must be :register, :map, :set, :flag or :counter 32 | """ 33 | def update(map, key_type, key, fun) when Record.is_record(map, :map) 34 | and is_atom(key_type) 35 | and is_binary(key) 36 | and is_function(fun, 1) do 37 | 38 | :riakc_map.update({key, key_type}, fun, map) 39 | end 40 | def update(nil, _, _, _), do: {:error, :nil_object} 41 | def update({:error, term}, _, _, _), do: {:error, term} 42 | 43 | @doc """ 44 | Update the `key` on the `map` by passing the `value` 45 | The value can be any other CRDT 46 | """ 47 | def put(map, key, value) when Record.is_record(map, :map) 48 | and is_binary(key) do 49 | key_type = Riak.CRDT.type(value) 50 | fun = fn _ -> value end 51 | :riakc_map.update({key, key_type}, fun, map) 52 | end 53 | def put(nil, _, _), do: {:error, :nil_object} 54 | def put({:error, term}, _, _), do: {:error, term} 55 | 56 | @doc """ 57 | Delete a `key` from the `map` 58 | """ 59 | def delete(map, key) when Record.is_record(map, :map) do 60 | :riakc_map.erase(key, map) 61 | end 62 | def delete(nil, _), do: {:error, :nil_object} 63 | def delete({:error, term}, _), do: {:error, term} 64 | 65 | @doc """ 66 | Get the original value of the `map` 67 | """ 68 | def value(map) when Record.is_record(map, :map), do: :riakc_map.value(map) 69 | def value(nil), do: {:error, :nil_object} 70 | def value({:error, term}), do: {:error, term} 71 | 72 | @doc """ 73 | List all keys of the `map` 74 | """ 75 | def keys(map) when Record.is_record(map, :map), do: :riakc_map.fetch_keys(map) 76 | def keys(nil), do: {:error, :nil_object} 77 | def keys({:error, term}), do: {:error, term} 78 | 79 | @doc """ 80 | Test if the `key` is contained in the `map` 81 | """ 82 | def has_key?(map, key) when Record.is_record(map, :map) do 83 | :riakc_map.is_key(key, map) 84 | end 85 | def has_key?(nil, _), do: {:error, :nil_object} 86 | def has_key?({:error, term}, _), do: {:error, term} 87 | end 88 | -------------------------------------------------------------------------------- /lib/riak/crdt/register.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.Register do 2 | @moduledoc """ 3 | Encapsulates a binary data to be used on CRDT.Map's 4 | """ 5 | require Record 6 | 7 | @doc """ 8 | Creates a new register 9 | """ 10 | def new(), do: :riakc_register.new() 11 | 12 | @doc """ 13 | Creates a new register with the initial `value` 14 | """ 15 | def new(value) when is_binary(value), do: set(new(), value) 16 | 17 | @doc """ 18 | Extracts current value of `register` 19 | """ 20 | def value(register) when Record.is_record(register, :register) do 21 | :riakc_register.value(register) 22 | end 23 | def value(nil), do: {:error, :nil_object} 24 | def value({:error, term}), do: {:error, term} 25 | 26 | @doc """ 27 | Set the `value` on the `register` 28 | """ 29 | def set(register, value) when Record.is_record(register, :register) 30 | and is_binary(value) do 31 | :riakc_register.set(value, register) 32 | end 33 | def set(nil, _), do: {:error, :nil_object} 34 | def set({:error, term}, _), do: {:error, term} 35 | end 36 | -------------------------------------------------------------------------------- /lib/riak/crdt/set.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.Set do 2 | @moduledoc """ 3 | Encapsulates riakc_set using elixir Set functions as interface. 4 | It accepts only binary as items. 5 | """ 6 | require Record 7 | 8 | @doc """ 9 | Create an empty set 10 | """ 11 | def new, do: :riakc_set.new 12 | def new(context), do: :riakc_set.new(context) 13 | def new(value, context) when is_list(value), do: :riakc_set.new(value, context) 14 | 15 | @doc """ 16 | Get original value as an `ordset` 17 | """ 18 | def value(set) when Record.is_record(set, :set) do 19 | :riakc_set.value(set) 20 | end 21 | def value(nil), do: {:error, :nil_object} 22 | def value({:error, term}), do: {:error, term} 23 | 24 | @doc """ 25 | Checks if `set` contains `value`. 26 | """ 27 | def member?(set, value) when Record.is_record(set, :set) and is_binary(value) do 28 | :riakc_set.is_element(value, set) 29 | end 30 | def member?(nil, _), do: {:error, :nil_object} 31 | def member?({:error, term}, _), do: {:error, term} 32 | 33 | @doc """ 34 | Insert `value` on `set` 35 | """ 36 | def put(set, value) when Record.is_record(set, :set) and is_binary(value) do 37 | :riakc_set.add_element(value, set) 38 | end 39 | def put(nil, _), do: {:error, :nil_object} 40 | def put({:error, term}, _), do: {:error, term} 41 | 42 | @doc """ 43 | Delete `value` on `set` 44 | """ 45 | def delete(set, value) when Record.is_record(set, :set) and is_binary(value) do 46 | :riakc_set.del_element(value, set) 47 | end 48 | def delete(nil, _), do: {:error, :nil_object} 49 | def delete({:error, term}, _), do: {:error, term} 50 | 51 | @doc """ 52 | Returns the number of elements in `set` 53 | """ 54 | def size(set) when Record.is_record(set, :set) do 55 | :riakc_set.size(set) 56 | end 57 | def size(nil), do: {:error, :nil_object} 58 | def size({:error, term}), do: {:error, term} 59 | end 60 | -------------------------------------------------------------------------------- /lib/riak/index.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Index do 2 | import Riak.Pool 3 | import :riakc_pb_socket 4 | 5 | @doc """ 6 | @deprecated There are performance issues with 2i, please use another query strategy. 7 | Execute a secondary index equality query. 8 | """ 9 | defpool query(pid, bucket, {type, name}, key, opts) when is_pid(pid) do 10 | name = String.to_charlist(name) 11 | response = get_index_eq(pid, bucket, {type, name}, key, opts) 12 | handle_query_response(response) 13 | end 14 | 15 | @doc """ 16 | @deprecated There are performance issues with 2i, please use another query strategy. 17 | Execute a secondary index range query. 18 | """ 19 | defpool query(pid, bucket, {type, name}, startkey, endkey, opts) when is_pid(pid) do 20 | name = String.to_charlist(name) 21 | response = get_index_range(pid, bucket, {type, name}, startkey, endkey, opts) 22 | handle_query_response(response) 23 | end 24 | 25 | defp handle_query_response(response) do 26 | case response do 27 | {:ok, {:index_results_v1, keys, terms, continuation}} -> 28 | {keys, terms, continuation} 29 | {:error, term} -> {:error, term} 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/riak/map_reduce.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Mapred do 2 | import Riak.Pool 3 | import :riakc_pb_socket 4 | 5 | @doc """ 6 | @deprecated There are performance issues with MapReduce, please use another query strategy. 7 | Perform a MapReduce job across the cluster. 8 | See the MapReduce documentation for explanation of behavior. 9 | """ 10 | defpool query(pid, inputs, query) when is_pid(pid), do: mapred(pid, inputs, query) 11 | defpool query(pid, inputs, query, timeout) when is_pid(pid), do: mapred(pid, inputs, query, timeout) 12 | 13 | defmodule Bucket do 14 | @doc """ 15 | @deprecated There are performance issues with MapReduce, please use another query strategy. 16 | Perform a MapReduce job across the cluster using an entire bucket as the input. 17 | See the MapReduce documentation for explanation of behavior. 18 | """ 19 | defpool query(pid, bucket, query) when is_pid(pid), do: mapred_bucket(pid, bucket, query) 20 | defpool query(pid, bucket, query, timeout) when is_pid(pid), do: mapred_bucket(pid, bucket, query, timeout) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/riak/object.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Object do 2 | @moduledoc """ 3 | The Data wrapper makes it convenient to work with Riak data in Elixir 4 | """ 5 | 6 | @doc """ 7 | Struct representing a Riak Object. Attributes: 8 | * `type`: String; Bucket Type with a unique name within the cluster namespace 9 | * `bucket`: String; Bucket with a unique name within the bucket type namespace 10 | * `key`: String; Not required; Key with a unique name within the bucket namespace 11 | * `data`: Any; Value to be stored under the key 12 | * `metadata`: Orddict; User specified metadata 13 | * `vclock`: String; Dotted Version Vector / Causal Context for object 14 | * `content_type`: String; Content Type for object 15 | """ 16 | defstruct [bucket: nil, type: nil, key: nil, data: nil, metadata: nil, vclock: nil, content_type: "application/json"] 17 | 18 | @doc """ 19 | Get all metadata entries 20 | """ 21 | def get_metadata(obj, key) do 22 | case :riakc_obj.get_user_metadata_entry( 23 | to_undefined(obj.metadata), to_undefined(key)) do 24 | :notfound -> nil 25 | val -> val 26 | end 27 | end 28 | 29 | def get_all_metadata(obj) do 30 | :riakc_obj.get_user_metadata_entries( 31 | to_undefined(obj.metadata)) 32 | end 33 | 34 | def delete_metadata(obj, key) do 35 | %{obj | metadata: :riakc_obj.delete_user_metadata_entry( 36 | to_undefined(obj.metadata), to_undefined(key))} 37 | end 38 | 39 | def delete_all_metadata(obj) do 40 | %{obj | metadata: :riakc_obj.clear_user_metadata_entries( 41 | to_undefined(obj.metadata))} 42 | end 43 | 44 | def put_metadata(obj, {key, value}) do 45 | %{obj | metadata: :riakc_obj.set_user_metadata_entry( 46 | to_undefined(obj.metadata), {key, value})} 47 | end 48 | 49 | # Secondary Index 50 | def index_id({:binary_index, name}), do: "#{name}_bin" 51 | def index_id({:integer_index, name}), do: "#{name}_int" 52 | 53 | def get_index(obj, {type, name}) do 54 | case :riakc_obj.get_secondary_index( 55 | to_undefined(obj.metadata), 56 | {to_undefined(type), to_undefined(name)}) do 57 | :notfound -> nil 58 | val -> val 59 | end 60 | end 61 | 62 | def get_all_indexes(obj) do 63 | :riakc_obj.get_secondary_indexes(to_undefined(obj.metadata)) 64 | end 65 | 66 | def delete_index(obj, {type, name}) do 67 | %{obj | metadata: :riakc_obj.delete_secondary_index( 68 | to_undefined(obj.metadata), 69 | {to_undefined(type), to_undefined(name)})} 70 | end 71 | 72 | def delete_all_indexes(obj) do 73 | %{obj | metadata: :riakc_obj.clear_secondary_indexes( 74 | to_undefined(obj.metadata))} 75 | end 76 | 77 | def put_index(obj, {type, name}, values) do 78 | %{obj | metadata: :riakc_obj.add_secondary_index( 79 | to_undefined(obj.metadata), 80 | [{{to_undefined(type), to_undefined(name)}, 81 | to_undefined(values)}])} 82 | end 83 | 84 | # Links 85 | def get_link(obj, tag) do 86 | case :riakc_obj.get_links( 87 | to_undefined(obj.metadata), tag) do 88 | :notfound -> nil 89 | val -> val 90 | end 91 | end 92 | 93 | def get_all_links(obj) do 94 | :riakc_obj.get_all_links( 95 | to_undefined(obj.metadata)) 96 | end 97 | 98 | def delete_link(obj, tag) do 99 | %{obj | metadata: :riakc_obj.delete_links( 100 | to_undefined(obj.metadata), tag)} 101 | end 102 | def delete_all_links(obj) do 103 | %{obj | metadata: :riakc_obj.clear_links( 104 | to_undefined(obj.metadata))} 105 | end 106 | 107 | def put_link(obj, tag, bucket, key) do 108 | %{obj | metadata: :riakc_obj.add_link( 109 | to_undefined(obj.metadata), 110 | [{tag, [{to_undefined(bucket),to_undefined(key)}]}])} 111 | end 112 | 113 | def from_robj(robj) do 114 | data = 115 | try do 116 | :riakc_obj.get_update_value(robj) 117 | catch 118 | :no_value -> nil 119 | e -> raise e 120 | end 121 | %Riak.Object{bucket: to_nil(:riakc_obj.bucket(robj)), 122 | type: to_nil(:riakc_obj.bucket_type(robj)), 123 | key: to_nil(:riakc_obj.key(robj)), 124 | data: to_nil(data), 125 | metadata: to_nil(:riakc_obj.get_update_metadata(robj)), 126 | vclock: to_nil(:riakc_obj.vclock(robj)), 127 | content_type: to_nil(:riakc_obj.get_update_content_type(robj))} 128 | end 129 | 130 | def to_robj(obj) do 131 | type = to_undefined(obj.type) 132 | bucket = to_bucket(obj.bucket) 133 | typed_bucket = 134 | case {type, bucket} do 135 | {:undefined, b} -> b; 136 | {t, {_, b}} -> {t, b}; 137 | {t, b} -> {t, b}; 138 | end 139 | robj = :riakc_obj.new( 140 | typed_bucket, 141 | to_undefined(obj.key), 142 | to_undefined(obj.data), 143 | to_undefined(obj.content_type)) 144 | robj = 145 | case to_undefined(obj.vclock) do 146 | :undefined -> robj; 147 | v -> :riakc_obj.set_vclock(robj, v) 148 | end 149 | robj = 150 | case to_undefined(obj.metadata) do 151 | :undefined -> robj; 152 | m -> :riakc_obj.update_metadata(robj, m) 153 | end 154 | 155 | robj 156 | end 157 | 158 | def create(args \\ [bucket: "default"]) do 159 | obj = struct(Riak.Object, args) 160 | from_robj(to_robj(obj)) 161 | end 162 | 163 | defp to_undefined(nil) do 164 | :undefined 165 | end 166 | defp to_undefined(v) do 167 | v 168 | end 169 | 170 | defp to_nil(:undefined) do 171 | nil 172 | end 173 | defp to_nil(v) do 174 | v 175 | end 176 | 177 | defp to_bucket({nil, b}) do 178 | to_undefined(b) 179 | end 180 | defp to_bucket({:undefined, b}) do 181 | to_undefined(b) 182 | end 183 | defp to_bucket(b) do 184 | to_undefined(b) 185 | end 186 | 187 | 188 | end 189 | -------------------------------------------------------------------------------- /lib/riak/pool.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Pool do 2 | @moduledoc """ 3 | [EXPERIMENTAL] 4 | This module adds defpool to define functions with a 5 | lower arity for each function so: 6 | 7 | Riak.put(pid, bucket, key, data) -> 8 | 9 | - Riak.put(bucket, key, data) that calls the previous function 10 | with a pid from the pool 11 | 12 | - Riak.put(pool_group, bucket, key, data) when is_atom(pool_group) 13 | that calls the previous function 14 | with a pid from the given pool name from the pool, instead of the default :riak group 15 | 16 | 17 | """ 18 | defmacro defpool(args, do: block) do 19 | {{name, _, args}, guards} = extract_guards(args) 20 | [_pid_arg | other_args] = args 21 | has_guards = (guards != []) 22 | quote do 23 | def unquote(name)(group_name,unquote_splicing(other_args)) when is_atom(group_name) do 24 | pid = take_group_member(group_name, 500) 25 | case pid do 26 | :error_no_members -> :connection_pool_exhausted 27 | _ -> 28 | result = unquote(name)(pid, unquote_splicing(other_args)) 29 | :pooler.return_group_member(:riak, pid, :ok) 30 | result 31 | end 32 | end 33 | if unquote(has_guards) do 34 | def unquote(name)(unquote_splicing(args)) when unquote(hd(guards)) do 35 | unquote(block) 36 | end 37 | else 38 | def unquote(name)(unquote_splicing(args)) do 39 | unquote(block) 40 | end 41 | end 42 | def unquote(name)(unquote_splicing(other_args)) do 43 | pid = take_group_member(:riak, 500) 44 | case pid do 45 | :error_no_members -> :connection_pool_exhausted 46 | _ -> 47 | result = unquote(name)(pid, unquote_splicing(other_args)) 48 | :pooler.return_group_member(:riak, pid, :ok) 49 | result 50 | end 51 | end 52 | end 53 | end 54 | 55 | def take_group_member(group_name, timeout \\ 100) 56 | def take_group_member(_, timeout) when timeout <= 0 do 57 | :error_no_members 58 | end 59 | def take_group_member(group_name, timeout) do 60 | case :pooler.take_group_member(group_name) do 61 | :error_no_members -> 62 | #should probably log a warning / error here so the operator knows something is wrong 63 | :timer.sleep(100) 64 | take_group_member(group_name, timeout - 100) 65 | pid -> pid 66 | end 67 | end 68 | 69 | defp extract_guards({:when, _, [left, right]}), do: {left, extract_or_guards(right)} 70 | defp extract_guards(else_), do: {else_, []} 71 | defp extract_or_guards({:when, _, [left, right]}), do: [left|extract_or_guards(right)]; 72 | defp extract_or_guards(term), do: [term] 73 | 74 | end 75 | -------------------------------------------------------------------------------- /lib/riak/search.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Search do 2 | import Riak.Pool 3 | import :riakc_pb_socket, except: [put: 2, put: 3, put: 4] 4 | 5 | defpool query(pid, index, query) when is_pid(pid), do: search(pid, index, query) 6 | defpool query(pid, index, query, options) when is_pid(pid), do: search(pid, index, query, options) 7 | defpool query(pid, index, query, options, timeout) when is_pid(pid), do: search(pid, index, query, options, timeout) 8 | 9 | defmodule Index do 10 | defpool list(pid) when is_pid(pid), do: list_search_indexes(pid) 11 | defpool put(pid, index) when is_pid(pid), do: create_search_index(pid, index) 12 | defpool put(pid, index, schema) when is_pid(pid), do: create_search_index(pid, index, schema, []) 13 | defpool put(pid, index, schema, props) when is_pid(pid), do: create_search_index(pid, index, schema, props) 14 | defpool get(pid, index) when is_pid(pid), do: get_search_index(pid, index) 15 | defpool delete(pid, index) when is_pid(pid), do: delete_search_index(pid, index) 16 | end 17 | 18 | defmodule Schema do 19 | defpool get(pid, schema) when is_pid(pid), do: get_search_schema(pid, schema) 20 | 21 | defpool create(pid, schema, content) when is_pid(pid) do 22 | create_search_schema(pid, schema, content) 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/riak/timeseries.ex: -------------------------------------------------------------------------------- 1 | defmodule Riak.Timeseries do 2 | import Riak.Pool 3 | 4 | defpool query(pid, query_text) when is_pid(pid) and is_binary(query_text) do 5 | query_list = :erlang.binary_to_list(query_text) 6 | case :riakc_ts.query(pid, query_list) do 7 | {:ok, results} -> results; 8 | err -> err 9 | end 10 | end 11 | 12 | defpool put(pid, table, data) when is_pid(pid) and is_list(data)do 13 | :riakc_ts.put(pid, table, data) 14 | end 15 | 16 | defpool get(pid, table, key) when is_pid(pid) and is_list(key) do 17 | get(pid, table, key, []) 18 | end 19 | 20 | defpool get(pid, table, key, options) when is_pid(pid) and is_list(key) do 21 | case :riakc_ts.get(pid, table, key, options) do 22 | {:ok, results} -> results; 23 | err -> err 24 | end 25 | end 26 | 27 | defpool delete(pid, table, key) when is_pid(pid) and is_list(key) do 28 | delete(pid, table, key, []) 29 | end 30 | 31 | defpool delete(pid, table, key, options) when is_pid(pid) and is_list(key) do 32 | :riakc_ts.delete(pid, table, key, options) 33 | end 34 | 35 | defpool list!(pid, table) when is_pid(pid) do 36 | list!(pid, table, []) 37 | end 38 | 39 | defpool list!(pid, table, options) when is_pid(pid) do 40 | {:ok, req_id} = :riakc_ts.stream_list_keys(pid, table, options) 41 | wait_for_list(req_id, []) 42 | end 43 | 44 | defp wait_for_list(req_id, acc) do 45 | receive do 46 | {^req_id, :done} -> List.flatten(acc) 47 | {^req_id, {:error, reason}} -> {:error, reason} 48 | {^req_id, {_, res}} -> wait_for_list(req_id, [res|acc]) 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ app: :riak, 6 | version: "1.1.6", 7 | build_per_environment: false, 8 | name: "Riak Elixir Client", 9 | source_url: "https://github.com/drewkerrigan/riak-elixir-client", 10 | deps: deps(), 11 | description: description(), 12 | package: package() ] 13 | end 14 | 15 | # Configuration for the OTP application 16 | def application do 17 | [ applications: [ :pooler ], 18 | included_applications: [ :riakc ] ] 19 | end 20 | 21 | defp deps do 22 | [ {:ex_doc, ">= 0.0.0", only: :dev}, 23 | {:earmark, ">= 0.0.0", only: :dev}, 24 | {:pooler, "~> 1.5.3"}, 25 | {:riakc, "~> 2.5.3"} ] 26 | end 27 | 28 | defp description do 29 | """ 30 | A Riak client written in Elixir. 31 | """ 32 | end 33 | 34 | defp package do 35 | [ files: ~w(config doc examples lib test LICENSE mix.exs mix.lock README.md THANKS), 36 | maintainers: [ "Drew Kerrigan", "Eduardo Gurgel", "Randy Secrist" ], 37 | licenses: [ "Apache 2.0" ], 38 | links: %{"GitHub" => "https://github.com/drewkerrigan/riak-elixir-client", 39 | "Documentation" => "http://hexdocs.pm/riak"} ] 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], [], "hexpm"}, 2 | "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, 3 | "hamcrest": {:hex, :basho_hamcrest, "0.4.1", "fb7b2c92d252a1e9db936750b86089addaebeb8f87967fb4bbdda61e8863338e", [:make, :mix, :rebar3], [], "hexpm"}, 4 | "linguist": {:hex, :linguist, "0.1.5", "98739fc47ac5d951322a57c787f46786c95b53d800105d43a0631633e7f9da9b", [:mix], [], "hexpm"}, 5 | "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:rebar, :make], []}, 6 | "pooler": {:hex, :pooler, "1.5.3", "898cd1fa301fc42d4a8ed598ce139b71ca85b54c16ab161152b5cc5fbdcfa1a8", [:rebar3], [], "hexpm"}, 7 | "protobuffs": {:hex, :protobuffs, "0.8.4", "d38ca5f7380d8477c274680273372011890f8d0037c0d7e7db5c0207b89a4e0b", [:rebar, :make], [{:meck, "~> 0.8.4", [hex: :meck, optional: false]}]}, 8 | "riak_pb": {:hex, :riak_pb, "2.3.2", "48ffbf66dbb3f136ab9a7134bac4e496754baa5ef58c4f50a61326736d996390", [:make, :mix, :rebar3], [{:hamcrest, "~> 0.4.1", [hex: :basho_hamcrest, repo: "hexpm", optional: false]}], "hexpm"}, 9 | "riakc": {:hex, :riakc, "2.5.3", "6132d9e687a0dfd314b2b24c4594302ca8b55568a5d733c491d8fb6cd4004763", [:make, :mix, :rebar3], [{:riak_pb, "~> 2.3", [hex: :riak_pb, repo: "hexpm", optional: false]}], "hexpm"}} 10 | -------------------------------------------------------------------------------- /test/bucket_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.BucketTest do 2 | use Riak.Case 3 | 4 | @moduletag :riak1 5 | @tag timeout: 10000 6 | 7 | test "list bucket", context do 8 | {:ok, buckets} = Riak.Bucket.list context[:pid] 9 | assert is_list(buckets) 10 | end 11 | 12 | test "list! bucket", context do 13 | buckets = Riak.Bucket.list! context[:pid] 14 | assert is_list(buckets) 15 | end 16 | 17 | test "list keys", context do 18 | {:ok, keys} = Riak.Bucket.keys context[:pid], "user" 19 | assert is_list(keys) 20 | end 21 | 22 | test "list! keys", context do 23 | keys = Riak.Bucket.keys! context[:pid], "user" 24 | assert is_list(keys) 25 | end 26 | 27 | test "bucket props", context do 28 | pid = context[:pid] 29 | assert :ok == Riak.Bucket.put pid, "user", [{:notfound_ok, false}] 30 | 31 | {:ok, props} = Riak.Bucket.get pid, "user" 32 | assert is_list(props) 33 | assert props[:notfound_ok] == false 34 | 35 | assert :ok == Riak.Bucket.reset pid, "user" 36 | 37 | {:ok, props} = Riak.Bucket.get pid, "user" 38 | assert props[:notfound_ok] == true 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/crdt_counter_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.CounterTest do 2 | use Riak.Case 3 | alias Riak.CRDT.Counter 4 | 5 | @moduletag :riak2 6 | 7 | test "create, update and find a counter" do 8 | key = Riak.Helper.random_key 9 | 10 | Counter.new 11 | |> Counter.increment 12 | |> Counter.increment(2) 13 | |> Riak.update("counters", "bucketcounter", key) 14 | 15 | counter = Riak.find("counters", "bucketcounter", key) 16 | |> Counter.value 17 | 18 | assert counter == 3 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/crdt_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDTTest do 2 | use ExUnit.Case 3 | import Riak.CRDT 4 | 5 | @moduletag :riak2 6 | 7 | test 'type of Register' do 8 | assert type(Riak.CRDT.Register.new) == :register 9 | end 10 | 11 | test 'type of Set' do 12 | assert type(Riak.CRDT.Set.new) == :set 13 | end 14 | 15 | test 'type of Map' do 16 | assert type(Riak.CRDT.Map.new) == :map 17 | end 18 | 19 | test 'type of Flag' do 20 | assert type(Riak.CRDT.Flag.new) == :flag 21 | end 22 | 23 | test 'type of Counter' do 24 | assert type(Riak.CRDT.Counter.new) == :counter 25 | end 26 | 27 | test 'type of HyperLogLog' do 28 | assert type(Riak.CRDT.HyperLogLog.new) == :hll 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/flag_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.FlagTest do 2 | use Riak.Case 3 | alias Riak.CRDT.Map, as: RiakMap 4 | alias Riak.CRDT.Flag 5 | 6 | @moduletag :riak2 7 | 8 | test "create a flag" do 9 | assert {:flag, false, :undefined, :undefined} = Flag.new 10 | end 11 | 12 | test "create and enable a flag" do 13 | flag = 14 | RiakMap.new 15 | |> RiakMap.put("foo", Flag.new) 16 | |> RiakMap.update(:flag, "foo", &Flag.enable/1) 17 | 18 | assert flag == { 19 | :map, [], 20 | [{{"foo", :flag}, {:flag, false, :enable, :undefined}}], 21 | [], 22 | :undefined 23 | } 24 | end 25 | 26 | test "create and disable a flag" do 27 | flag = 28 | RiakMap.new 29 | |> RiakMap.put("foo", Flag.new("somecontext")) 30 | |> RiakMap.update(:flag, "foo", &Flag.disable/1) 31 | 32 | assert flag == { 33 | :map, [], 34 | [{{"foo", :flag}, {:flag, false, :disable, "somecontext"}}], 35 | [], 36 | :undefined 37 | } 38 | end 39 | 40 | test "create and enable a flag, save then get value" do 41 | RiakMap.new 42 | |> RiakMap.put("flag_key_true", Flag.new |> Flag.enable) 43 | |> Riak.update("maps", "flagbucket", "flagmap") 44 | 45 | flag_value = Riak.find("maps", "flagbucket", "flagmap") 46 | |> RiakMap.get(:flag, "flag_key_true") 47 | 48 | assert flag_value 49 | end 50 | 51 | test "create and disable a flag, save then get value" do 52 | RiakMap.new 53 | |> RiakMap.put("flag_key_false", Flag.new |> Flag.enable) 54 | |> Riak.update("maps", "flagbucket", "flagmap2") 55 | 56 | Riak.find("maps", "flagbucket", "flagmap2") 57 | |> RiakMap.update(:flag, "flag_key_false", &Flag.disable/1) 58 | |> Riak.update("maps", "flagbucket", "flagmap2") 59 | 60 | flag3 = Riak.find("maps", "flagbucket", "flagmap2") 61 | 62 | # Assert flag is false after we saved the disabled one 63 | assert RiakMap.get(flag3, :flag, "flag_key_false") == false 64 | end 65 | 66 | end 67 | -------------------------------------------------------------------------------- /test/hll_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.HyperLogLogTest do 2 | use Riak.Case 3 | alias Riak.CRDT.HyperLogLog 4 | 5 | @moduletag :riak2 6 | 7 | test "create, update and find a hll" do 8 | key = Riak.Helper.random_key 9 | 10 | HyperLogLog.new 11 | |> HyperLogLog.add_element("One") 12 | |> HyperLogLog.add_elements(["Two", "Three", "One", "Two"]) 13 | |> Riak.update("hll", "buckethll", key) 14 | 15 | counter = Riak.find("hll", "buckethll", key) 16 | |> HyperLogLog.value 17 | 18 | assert counter == 3 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/map_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.MapTest do 2 | require IEx 3 | use Riak.Case 4 | alias Riak.CRDT.Map 5 | alias Riak.CRDT.Register 6 | alias Riak.CRDT.Flag 7 | alias Riak.CRDT.Counter 8 | alias Riak.CRDT.Set 9 | 10 | @moduletag :riak2 11 | 12 | test "create, update and find a map with other CRDTs" do 13 | key = Riak.Helper.random_key 14 | 15 | reg_data = "Register data" 16 | reg = Register.new(reg_data) 17 | reg_key = "register_key" 18 | 19 | flag = Flag.new |> Flag.enable 20 | flag_key = "flag_key" 21 | 22 | counter = Counter.new |> Counter.increment 23 | counter_key = "counter_key" 24 | 25 | set = Set.new |> Set.put("foo") 26 | set_key = "set_key" 27 | 28 | Map.new 29 | |> Map.put(reg_key, reg) 30 | |> Map.put(flag_key, flag) 31 | |> Map.update(:counter, counter_key, fn _ -> counter end) 32 | |> Map.put(set_key, set) 33 | |> Riak.update("maps", "bucketmap", key) 34 | 35 | map = Riak.find("maps", "bucketmap", key) 36 | |> Map.value 37 | 38 | map_keys = :orddict.fetch_keys(map) 39 | assert {"counter_key", :counter} in map_keys 40 | assert {"flag_key", :flag} in map_keys 41 | assert {"register_key", :register} in map_keys 42 | assert {"set_key", :set} in map_keys 43 | 44 | assert :orddict.size(map) == 4 45 | 46 | data = :orddict.to_list(map) 47 | assert {{reg_key, :register}, reg_data} in data 48 | assert {{flag_key, :flag}, true} in data 49 | assert {{counter_key, :counter}, 1} in data 50 | assert {{set_key, :set}, ["foo"]} in data 51 | end 52 | 53 | test "create, update and find nested maps" do 54 | key = Riak.Helper.random_key 55 | 56 | flag = Flag.new |> Flag.enable 57 | flag_key = "flag_key" 58 | 59 | nested = Map.new |> Map.put(flag_key, flag) 60 | nested_key = "nested_key" 61 | 62 | Map.new 63 | |> Map.put(nested_key, nested) 64 | |> Riak.update("maps", "bucketmap", key) 65 | 66 | map = Riak.find("maps", "bucketmap", key) 67 | 68 | value_map = map |> Map.value 69 | 70 | assert :orddict.size(value_map) == 1 71 | assert :orddict.fetch({nested_key, :map}, value_map) == [{{flag_key, :flag}, true}] 72 | 73 | exists = Riak.find("maps", "bucketmap", key) 74 | |> Map.has_key?({nested_key, :map}) 75 | 76 | assert exists == true 77 | 78 | Riak.find("maps", "bucketmap", key) |> Map.delete({nested_key, :map}) 79 | |> Riak.update("maps", "bucketmap", key) 80 | 81 | exists = Riak.find("maps", "bucketmap", key) 82 | |> Map.has_key?({nested_key, :map}) 83 | 84 | assert exists == false 85 | 86 | end 87 | 88 | test "create, update, delete map" do 89 | key = Riak.Helper.random_key 90 | 91 | Map.new 92 | |> Map.put("register_key", Register.new("Some Data")) 93 | |> Riak.update("maps", "users", key) 94 | 95 | reg_data = Riak.find("maps", "users", key) 96 | |> Map.get(:register, "register_key") 97 | 98 | assert "Some Data" == reg_data 99 | 100 | Riak.delete("maps", "users", key) 101 | assert Riak.find("maps", "users", key) == nil 102 | 103 | end 104 | 105 | test "map key exists" do 106 | key = Riak.Helper.random_key 107 | 108 | Map.new 109 | |> Map.put("register_key", Register.new("Some Data")) 110 | |> Riak.update("maps", "users", key) 111 | 112 | exists = Riak.find("maps", "users", key) 113 | |> Map.has_key?({"nothere", :register}) 114 | 115 | assert exists == false 116 | 117 | exists = Riak.find("maps", "users", key) 118 | |> Map.has_key?({"register_key", :register}) 119 | 120 | assert exists == true 121 | 122 | keys = Riak.find("maps", "users", key) 123 | |> Map.keys() 124 | 125 | assert keys == [{"register_key", :register}] 126 | end 127 | end 128 | -------------------------------------------------------------------------------- /test/pool_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.PoolTest do 2 | use Riak.Case 3 | import Riak.Helper 4 | 5 | @moduletag :riak1 6 | 7 | test "put" do 8 | key = random_key() 9 | 10 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 11 | 12 | assert Riak.put(o) == o 13 | assert Riak.put(:riak, o) == o 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/riak_test.exs: -------------------------------------------------------------------------------- 1 | defmodule RiakTest do 2 | use Riak.Case 3 | 4 | @moduletag :riak1 5 | 6 | test "put", context do 7 | pid = context[:pid] 8 | key = Riak.Helper.random_key 9 | 10 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 11 | 12 | assert Riak.put(pid, o) == o 13 | end 14 | 15 | test "find", context do 16 | pid = context[:pid] 17 | key = Riak.Helper.random_key 18 | 19 | data = "Drew Kerrigan" 20 | o = Riak.Object.create(bucket: "user", key: key, data: data) 21 | Riak.put(pid, o) 22 | 23 | assert Riak.find(pid, "user", key).data == o.data 24 | end 25 | 26 | # curl -XDELETE http://localhost:8098/types/spot/buckets/active/keys/EURUSD 27 | @tag :skip 28 | test "find with deleted", context do 29 | 30 | pid = context[:pid] 31 | key = Riak.Helper.random_key 32 | data = "Drew_Kerrigan" 33 | 34 | target = "http://localhost:8098/types/user_types/buckets/user/keys" 35 | System.cmd("curl", ~w[-s -XPUT #{target}/#{key} -d {"value_s":"#{data}_1"} -H Content-Type:application/json]) 36 | System.cmd("curl", ~w[-s -XDELETE #{target}/#{key}]) 37 | System.cmd("curl", ~w[-s -XPUT #{target}/#{key} -d {"value_s":"#{data}_2"} -H Content-Type:application/json]) 38 | 39 | assert Riak.find(pid, {"user_types", "user"}, key).data == ~s[{"value_s":"#{data}_2"}] 40 | end 41 | 42 | test "delete", context do 43 | pid = context[:pid] 44 | key = Riak.Helper.random_key 45 | 46 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 47 | Riak.put(pid, o) 48 | 49 | assert Riak.delete(pid, o) == :ok 50 | end 51 | 52 | test "crud operations and siblings", context do 53 | pid = context[:pid] 54 | key = Riak.Helper.random_key 55 | 56 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 57 | u = Riak.put(pid, o) 58 | 59 | assert u != nil 60 | 61 | assert :ok == Riak.delete pid, "user", u.key 62 | 63 | u = Riak.Object.create(bucket: "user", data: "Drew Kerrigan") 64 | assert u.key == nil 65 | u = Riak.put pid, u 66 | assert u.key != nil 67 | 68 | # Get the object again so we don't create a sibling 69 | u = Riak.find pid, "user", u.key 70 | 71 | o = %{u | data: "Something Else"} 72 | u = Riak.put pid, o 73 | 74 | unewdata = Riak.find pid, "user", u.key 75 | 76 | assert unewdata.data == "Something Else" 77 | 78 | assert :ok == Riak.delete pid, "user", u.key 79 | assert :ok == Riak.delete pid, "user", key 80 | 81 | assert nil == Riak.find pid, "user", key 82 | end 83 | 84 | # test "create siblings", context do 85 | # pid = context[:pid] 86 | # key = Riak.Helper.random_key 87 | 88 | # o = Riak.Object.create(type: "siblings", bucket: "user", key: key, data: "Drew Kerrigan") 89 | # Riak.put(pid, o) 90 | # Riak.put(pid, o) 91 | # u = Riak.find(pid, {"siblings", "user"}, key) 92 | # u = case u do 93 | # nil -> Riak.find(pid, {"siblings", "user"}, key) 94 | # u -> u 95 | # end 96 | # assert u != nil 97 | # assert is_list(u) 98 | # Riak.resolve(pid, {"siblings", "user"}, key, Riak.Helper.index_of("Drew Kerrigan", u)) 99 | # u = Riak.find(pid, {"siblings", "user"}, key) 100 | # assert u != nil 101 | # assert u.data == "Drew Kerrigan" 102 | # assert :ok == Riak.delete pid, u 103 | # end 104 | 105 | test "user metadata", context do 106 | pid = context[:pid] 107 | key = Riak.Helper.random_key 108 | 109 | mdtest = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 110 | |> Riak.Object.put_metadata({"my_key", "my_value"}) 111 | |> Riak.Object.put_metadata({"my_key2", "my_value2"}) 112 | 113 | mdtest = Riak.put(pid, mdtest) 114 | |> Riak.Object.get_metadata("my_key") 115 | 116 | assert mdtest == "my_value" 117 | 118 | u = Riak.find pid, "user", key 119 | 120 | mdtest2 = u 121 | |> Riak.Object.get_metadata("my_key2") 122 | 123 | assert mdtest2 == "my_value2" 124 | 125 | mdtest3 = u 126 | |> Riak.Object.get_all_metadata() 127 | |> is_list 128 | 129 | assert mdtest3 130 | 131 | u = Riak.Object.delete_metadata(u, "my_key") 132 | 133 | assert nil == Riak.Object.get_metadata(u, "my_key") 134 | assert "my_value2" == Riak.Object.get_metadata(u, "my_key2") 135 | 136 | u = Riak.Object.delete_all_metadata(u) 137 | 138 | assert nil == Riak.Object.get_metadata(u, "my_key2") 139 | assert [] == Riak.Object.get_all_metadata(u) 140 | end 141 | 142 | test "secondary indexes", context do 143 | pid = context[:pid] 144 | key = Riak.Helper.random_key 145 | 146 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 147 | |> Riak.Object.put_index({:binary_index, "first_name"}, ["Drew"]) 148 | |> Riak.Object.put_index({:binary_index, "last_name"}, ["Kerrigan"]) 149 | Riak.put(pid, o) 150 | 151 | assert Riak.Object.get_index(o, {:binary_index, "first_name"}) == ["Drew"] 152 | 153 | case Riak.Index.query(pid, "user", {:binary_index, "first_name"}, "Drew", []) do 154 | {:error, "{error,{indexes_not_supported,riak_kv_bitcask_backend}}"} -> :ok 155 | {keys, terms, continuation} -> 156 | assert is_list(keys) 157 | assert terms == :undefined 158 | assert continuation == :undefined 159 | {keys, terms, continuation} = Riak.Index.query(pid, "user", {:binary_index, "last_name"}, "Kerrigam", "Kerrigao", []) 160 | assert is_list(keys) 161 | assert terms == :undefined 162 | assert continuation == :undefined 163 | end 164 | 165 | o = Riak.Object.delete_index(o, {:binary_index, "first_name"}) 166 | Riak.put(pid, o) 167 | 168 | assert Riak.Object.get_index(o, {:binary_index, "first_name"}) == nil 169 | 170 | assert is_list(Riak.Object.get_all_indexes(o)) 171 | 172 | indextest = o |> Riak.Object.delete_all_indexes 173 | |> Riak.Object.get_all_indexes 174 | 175 | assert indextest == [] 176 | end 177 | 178 | test "links", context do 179 | pid = context[:pid] 180 | 181 | o1 =Riak.Object.create(bucket: "user", key: "drew1", data: "Drew1 Kerrigan") 182 | Riak.put(pid, o1) 183 | o2 = Riak.Object.create(bucket: "user", key: "drew2", data: "Drew2 Kerrigan") 184 | Riak.put(pid, o2) 185 | 186 | key = Riak.Helper.random_key 187 | 188 | o = Riak.Object.create(bucket: "user", key: key, data: "Drew Kerrigan") 189 | |> Riak.Object.put_link("my_tag", "user", "drew1") 190 | |> Riak.Object.put_link("my_tag", "user", "drew2") 191 | Riak.put(pid, o) 192 | 193 | assert Riak.Object.get_link(o, "my_tag") == [{"user", "drew1"}, {"user", "drew2"}] 194 | 195 | assert Riak.Object.delete_link(o, "my_tag") |> Riak.Object.get_link("my_tag") == nil 196 | 197 | # Get the object again so we don't create a sibling 198 | o = Riak.find pid, "user", key 199 | 200 | o |> Riak.Object.put_link("my_tag", "user", "drew1") 201 | |> Riak.Object.put_link("my_tag", "user", "drew2") 202 | Riak.put(pid, o) 203 | 204 | assert Riak.Object.get_link(o, "my_tag") == [{"user", "drew1"}, {"user", "drew2"}] 205 | 206 | assert is_list(Riak.Object.get_all_links(o)) 207 | assert Riak.Object.delete_all_links(o) |> Riak.Object.get_all_links == [] 208 | end 209 | 210 | test "ping", context do 211 | assert Riak.ping(context[:pid]) == :pong 212 | end 213 | 214 | test "siblings", context do 215 | pid = context[:pid] 216 | assert :ok == Riak.Bucket.put pid, "user", [{:allow_mult, true}] 217 | 218 | key = Riak.Helper.random_key 219 | 220 | o1 = Riak.Object.create(bucket: "user", key: key, data: "Drew1 Kerrigan") 221 | Riak.put(pid, o1) 222 | o2 = Riak.Object.create(bucket: "user", key: key, data: "Drew2 Kerrigan") 223 | Riak.put(pid, o2) 224 | 225 | u = Riak.find pid, "user", key 226 | 227 | assert is_list(u) 228 | 229 | [h|_t] = u 230 | 231 | assert :ok == Riak.resolve(pid, "user", key, 2) 232 | 233 | u = Riak.find pid, "user", key 234 | 235 | assert u.data == h 236 | 237 | assert :ok == Riak.Bucket.reset pid, "user" 238 | end 239 | end 240 | -------------------------------------------------------------------------------- /test/set_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.CRDT.SetTest do 2 | use Riak.Case 3 | alias Riak.CRDT.Set 4 | 5 | @moduletag :riak2 6 | 7 | test "create, update and find a set" do 8 | key = Riak.Helper.random_key 9 | 10 | Set.new 11 | |> Set.put("foo") 12 | |> Set.put("bar") 13 | |> Riak.update("sets", "bucketset", key) 14 | 15 | set = Riak.find("sets", "bucketset", key) 16 | |> Set.value 17 | 18 | assert "foo" in set 19 | assert "bar" in set 20 | end 21 | 22 | test "size" do 23 | key = Riak.Helper.random_key 24 | 25 | Set.new 26 | |> Set.put("foo") |> Set.put("bar") 27 | |> Set.put("foo") |> Set.put("bar") 28 | |> Riak.update("sets", "bucketset", key) 29 | 30 | size = Riak.find("sets", "bucketset", key) 31 | |> Set.size 32 | 33 | assert size == 2 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.configure(exclude: [skip: true]) 2 | ExUnit.start 3 | 4 | defmodule Riak.Case do 5 | use ExUnit.CaseTemplate 6 | 7 | setup do 8 | {:ok, pid} = Riak.Connection.start('127.0.0.1', 8087) 9 | on_exit fn -> 10 | #Riak.Helper.clean! pid 11 | Process.exit(pid, :kill) 12 | end 13 | {:ok, pid: pid} 14 | end 15 | end 16 | 17 | defmodule Riak.Helper do 18 | def clean!(pid) do 19 | # This is terrible, and should not be used. 20 | for bucket <- Riak.Bucket.list!(pid), key <- Riak.Bucket.keys!(pid, bucket) do 21 | Riak.delete(pid, bucket, key) 22 | end 23 | end 24 | 25 | def random_key do 26 | {me, se, mi} = :erlang.timestamp 27 | "#{me}#{se}#{mi}" 28 | end 29 | 30 | # helper for chosing the index of a sibling value list 31 | def index_of(search, [search|_], index) do 32 | index 33 | end 34 | def index_of(search, [_|rest], index) do 35 | index_of(search, rest, index+1) 36 | end 37 | def index_of(search, haystack) do 38 | index_of(search, haystack, 1) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /test/timeseries_test.exs: -------------------------------------------------------------------------------- 1 | defmodule Riak.TimeseriesTest do 2 | use Riak.Case 3 | alias Riak.Timeseries 4 | 5 | @moduletag :riakts 6 | 7 | test "Insert TS records and query them" do 8 | assert :ok == Timeseries.put("GeoCheckin", [ 9 | {"region1", "state1", 25, "hot", 23.0}, 10 | {"region2", "state99", 26, "windy", 19.0} 11 | ]) 12 | 13 | e1 = {["region", "state", "time", "weather", "temperature"], [{"region1", "state1", 25, "hot", 23.0}]} 14 | r1 = Timeseries.query("select * from GeoCheckin where time > 24 and time < 26 and region = 'region1' and state = 'state1'") 15 | 16 | e2 = {["region", "state", "time", "weather", "temperature"], 17 | [{"region2", "state99", 26, "windy", 19.0}]} 18 | r2 = Timeseries.query("select * from GeoCheckin where time > 25 and time < 27 and region = 'region2' and state = 'state99'") 19 | 20 | r3_results = Timeseries.list!("GeoCheckin") 21 | r3 = contains_row( 22 | r3_results, {"region1", "state1", 25}) or contains_row( 23 | r3_results, {"region2", "state99", 26}) 24 | e3 = true 25 | 26 | e4 = {["region", "state", "time", "weather", "temperature"], 27 | [{"region1", "state1", 25, "hot", 23.0}]} 28 | r4 = Timeseries.get("GeoCheckin", ["region1", "state1", 25]) 29 | 30 | e5 = {["region", "state", "time", "weather", "temperature"], 31 | [{"region2", "state99", 26, "windy", 19.0}]} 32 | r5 = Timeseries.get("GeoCheckin", ["region2", "state99", 26]) 33 | 34 | e6 = :ok 35 | r6 = Timeseries.delete("GeoCheckin", ["region1", "state1", 25]) 36 | 37 | e7 = :ok 38 | r7 = Timeseries.delete("GeoCheckin", ["region2", "state99", 26]) 39 | 40 | e8 = {[], []} 41 | r8 = Timeseries.get("GeoCheckin", ["region1", "state1", 25]) 42 | 43 | e9 = {[], []} 44 | r9 = Timeseries.get("GeoCheckin", ["region2", "state99", 26]) 45 | 46 | e10 = {[],[]} 47 | r10 = Timeseries.query("select * from GeoCheckin where time > 25 and time < 27 and region = 'region2' and state = 'state99'") 48 | 49 | assert e1 == r1 50 | assert e2 == r2 51 | assert e3 == r3 52 | assert e4 == r4 53 | assert e5 == r5 54 | assert e6 == r6 55 | assert e7 == r7 56 | assert e8 == r8 57 | assert e9 == r9 58 | assert e10 == r10 59 | end 60 | 61 | defp contains_row([_], _) do 62 | false 63 | end 64 | defp contains_row([row|_], row) do 65 | true 66 | end 67 | defp contains_row([_|rest], row) do 68 | contains_row(rest, row) 69 | end 70 | end 71 | --------------------------------------------------------------------------------