relix 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.md +176 -0
  2. data/lib/relix.rb +8 -0
  3. data/lib/relix/version.rb +3 -0
  4. metadata +75 -0
data/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # Relix
2
+
3
+ A Redis-backed indexing layer that can be used with any (or no) backend data storage.
4
+
5
+ ## Rationale
6
+
7
+ With the rise in popularity of non-relational databases, and the regular use of relational databases in non-relational ways, data indexing has become an aspect of data storage that you can't simply assume is handled for you. More and more applications are storing their data in databases that treat that stored data as opaque, and thus there's no query engine sitting on top of the data making sure that it can be quickly and flexibly looked up.
8
+
9
+ Relix is a layer that can be added on to any model to make all the normal types of querying you want to do: equality, less than/greater than, in set, range, limit, etc., quick and painless. Relix depends on Redis to be awesome at what it does - blazingly fast operations on basic data types - and layers on top of that pluggable indexing of your data for fast lookup.
10
+
11
+ ## Philosophy
12
+
13
+ * Performance is paramount - be FAST.
14
+ * Leverage Redis and its strengths to the hilt. Never do in Relix what could be done in Redis.
15
+ * Be extremely tolerant to failure.
16
+ ** Since we can't guarantee atomicity with the backing datastore, index early and clean up later.
17
+ ** Make continuous index repair easy since the chaos monkey could attack at any time.
18
+ * Be pluggable; keep the core simple and allow easy extensibility
19
+
20
+ ## Installation
21
+
22
+ If you're using bundler, add Relix to your Gemfile:
23
+
24
+ gem 'relix'
25
+
26
+ Otherwise gem install:
27
+
28
+ $ gem install relix
29
+
30
+ ## Usage
31
+
32
+ To index something in a model, include the Relix module, declare the primary key (required), and declare any additional indexes you want:
33
+
34
+ class Transaction
35
+ include Relix
36
+
37
+ attr_accessor :key, :account_key, :created_at
38
+
39
+ relix do
40
+ primary_key :key
41
+ multi :account_key, order: :created_at
42
+ unique :by_created_at, on: :key, order: :created_at
43
+ end
44
+
45
+ def initialize(key, account_key, created_at)
46
+ @key = key
47
+ @account_key = account_key
48
+ @created_at = created_at
49
+
50
+ # Trigger the actual indexing
51
+ index!
52
+ end
53
+ end
54
+
55
+ Transaction.new(1, 1, Time.parse('2011-09-30'))
56
+ Transaction.new(2, 2, Time.parse('2011-09-29'))
57
+ Transaction.new(3, 2, Time.parse('2011-10-01'))
58
+ Transaction.new(4, 2, Time.parse('2011-08-30'))
59
+
60
+ Note the #index! call to trigger actual indexing.
61
+
62
+ Now that your indexes are declared, you can use an index to do a lookups:
63
+
64
+ p Transaction.lookup{|q| q[:account_key].eq(1) } # => [1]
65
+ p Transaction.lookup{|q| q[:account_key].eq(2) } # => [4,2,3]
66
+
67
+ The result is always an array of primary keys. You can also use a bare lookup to return all records:
68
+
69
+ p Transaction.lookup # => [1,2,3,4]
70
+
71
+ # Also useful for counting:
72
+ p Transaction.lookup.size # => 4
73
+
74
+ Some indexes can be ordered by default:
75
+
76
+ p Transaction.lookup{|q| q[:account_key].eq(2)} # => [4,2,3]
77
+
78
+ Which can be combined with offset and limit:
79
+
80
+ p Transaction.lookup{|q| q[:account_key].eq(2, limit: 1)} # => [4]
81
+ p Transaction.lookup{|q| q[:account_key].eq(2, limit: 1, offset: 1)} # => [2]
82
+ p Transaction.lookup{|q| q[:account_key].eq(2, limit: 1, offset: 2)} # => [3]
83
+
84
+ Since the :primary_key index is ordered by insertion order, we've also declared a :by_created_at index on key that gives us the records ordered by the #created_at attribute:
85
+
86
+ p Transaction.lookup{|q| q[:by_created_at].all} # => [4,2,1,3]
87
+
88
+ ## Querying
89
+
90
+ Relix uses a simple query language based on method chaining. A "root" query is passed in to the lookup block, and then query terms are chained off of it:
91
+
92
+ class Person
93
+ include Relix
94
+ relix do
95
+ primary_key :key
96
+ multi :name, order: :birthdate
97
+ end
98
+ end
99
+
100
+ people = Person.lookup{|q| q[:name].eq("Bob Smith")}
101
+
102
+ Basically you just specify an index and an operation against that index. In addition, you can specify options for the query, such as limit and offset, if supported by the index type. Relix only supports querying by a single index at a time.
103
+
104
+ Any ordered index can also be offset and limited:
105
+
106
+ people = Person.lookup{|q| q[:name].eq("Bob Smith", offset: 5, limit: 5)}
107
+
108
+ In addition, rather than an offset, an indexed primary key can be specified as a starting place using from:
109
+
110
+ person_id = Person.lookup{|q| q[:name].eq("Bob Smith")[2]}
111
+ people = Person.lookup{|q| q[:name].eq("Bob Smith", from: person_id)}
112
+
113
+ The from option is exclusive - it does not return or count the key you pass to it.
114
+
115
+
116
+ ## Indexing
117
+
118
+ ### Inheritance
119
+
120
+ Indexes are inherited up the Ruby ancestor chain, so you can for instance set the primary_key in a base class and then not have to re-declare it in each subclass.
121
+
122
+
123
+ ### Multiple Value Indexes
124
+
125
+ Indexes can be built over multiple attributes:
126
+
127
+ relix do
128
+ multi :storage_state_by_account, on: %w(storage_state account_id)
129
+ end
130
+
131
+ When there are multiple attributes, they are specified in a hash:
132
+
133
+ lookup do |q|
134
+ q[:storage_state_by_account].eq(
135
+ {storage_state: 'cached', account_id: 'bob'}, limit: 10)
136
+ end
137
+
138
+
139
+ ## Index Types
140
+
141
+ ### PrimaryKeyIndex
142
+
143
+ The primary key index is the only index that is required on a model. Under the covers it is stored very similarly to a UniqueIndex, and it is stably sorted in insertion order. It is declared using #primary_key within the relix block:
144
+
145
+ relix do
146
+ primary_key :id
147
+ end
148
+
149
+ **Supported Operators**: eq, all
150
+ **Ordering**: insertion
151
+
152
+
153
+ ### MultiIndex
154
+
155
+ Multi indexes allow multiple matching primary keys per indexed value, and are ideal for one to many relationships. They can include an ordering, and are declared using #multi in the relix block:
156
+
157
+ relix do
158
+ multi :account_id, order: :created_at
159
+ end
160
+
161
+ **Supported Operators**: eq
162
+ **Ordering**: can be ordered on any numeric attribute (default is the to_i of the indexed value)
163
+
164
+
165
+ ### UniqueIndex
166
+
167
+ Unique indexes will raise an error if the same value is indexed twice for a different primary key. They also provide super fast lookups. They are declared using #unique in the relix block:
168
+
169
+ relix do
170
+ unique :email
171
+ end
172
+
173
+ Unique indexes ignore nil values - they will not be indexed and an error is not raised if there is more than one object with a value of nil. A multi-value unique index will be completely skipped if any value in it is nil.
174
+
175
+ **Supported Operators**: eq, all
176
+ **Ordering**: can be ordered on any numeric attribute (default is the to_i of the indexed value)
data/lib/relix.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'relix/core'
2
+ require 'relix/redis'
3
+ require 'relix/query'
4
+ require 'relix/index_set'
5
+ require 'relix/index'
6
+ require 'relix/indexes/multi'
7
+ require 'relix/indexes/unique'
8
+ require 'relix/indexes/primary_key'
@@ -0,0 +1,3 @@
1
+ module Relix
2
+ VERSION = "1.0.0"
3
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relix
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathaniel Talbott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-04 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: hiredis
16
+ requirement: &70323249027760 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.3.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70323249027760
25
+ - !ruby/object:Gem::Dependency
26
+ name: redis
27
+ requirement: &70323249027180 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 2.2.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70323249027180
36
+ description: ! 'Relix is a layer that can be added on to any model to make all the
37
+ normal types of querying you want to do: equality, less than/greater than, in set,
38
+ range, limit, etc., quick and painless. Relix depends on Redis to be awesome at
39
+ what it does - blazingly fast operations on basic data types - and layers on top
40
+ of that pluggable indexing of your data for fast lookup.'
41
+ email:
42
+ - nathaniel@talbott.ws
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.md
48
+ - lib/relix.rb
49
+ - lib/relix/version.rb
50
+ homepage: http://github.com/ntalbott/relix
51
+ licenses: []
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ! '>='
66
+ - !ruby/object:Gem::Version
67
+ version: 1.8.6
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 1.8.11
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: A Redis-backed indexing layer that can be used with any (or no) backend data
74
+ storage.
75
+ test_files: []