hari 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/hari.gemspec +10 -2
  3. data/lib/hari.rb +8 -4
  4. data/lib/hari/configuration/redis.rb +6 -2
  5. data/lib/hari/entity.rb +10 -20
  6. data/lib/hari/entity/property.rb +19 -4
  7. data/lib/hari/entity/property/builder.rb +18 -3
  8. data/lib/hari/entity/repository.rb +11 -3
  9. data/lib/hari/entity/serialization.rb +53 -9
  10. data/lib/hari/entity/serialization/array.rb +21 -0
  11. data/lib/hari/entity/serialization/hash.rb +31 -0
  12. data/lib/hari/keys.rb +5 -0
  13. data/lib/hari/keys/hash.rb +67 -0
  14. data/lib/hari/keys/key.rb +24 -3
  15. data/lib/hari/keys/list.rb +10 -8
  16. data/lib/hari/keys/set.rb +8 -8
  17. data/lib/hari/keys/sorted_set.rb +20 -8
  18. data/lib/hari/node.rb +19 -2
  19. data/lib/hari/node/index.rb +152 -0
  20. data/lib/hari/node/queries.rb +32 -17
  21. data/lib/hari/node/queries/relation.rb +14 -1
  22. data/lib/hari/node/queries/relation/backend/sorted_set.rb +41 -90
  23. data/lib/hari/node/queries/relation/backend/sorted_set/count_step.rb +16 -0
  24. data/lib/hari/node/queries/relation/backend/sorted_set/node_step.rb +91 -0
  25. data/lib/hari/node/queries/type.rb +69 -4
  26. data/lib/hari/node/repository.rb +36 -0
  27. data/lib/hari/node/serialization.rb +11 -10
  28. data/lib/hari/object.rb +6 -0
  29. data/lib/hari/serialization.rb +3 -0
  30. data/lib/hari/version.rb +1 -1
  31. data/spec/hari/entity/repository_spec.rb +17 -0
  32. data/spec/hari/entity/serialization/hash_spec.rb +16 -0
  33. data/spec/hari/entity/serialization_spec.rb +14 -4
  34. data/spec/hari/keys/hash_spec.rb +55 -0
  35. data/spec/hari/keys/lists_spec.rb +27 -0
  36. data/spec/hari/node/index_spec.rb +199 -0
  37. data/spec/hari/node_spec.rb +84 -0
  38. data/spec/hari/serialization_spec.rb +41 -0
  39. data/spec/spec_helper.rb +6 -2
  40. metadata +27 -4
@@ -2,10 +2,10 @@ module Hari
2
2
  module Keys
3
3
  class Key
4
4
 
5
- attr_reader :node, :name
5
+ attr_reader :node, :name, :options
6
6
 
7
- def initialize(node = nil)
8
- @node = node
7
+ def initialize(node = nil, options = {})
8
+ @node, @options = node, options
9
9
  end
10
10
 
11
11
  def key
@@ -43,6 +43,27 @@ module Hari
43
43
  Hari.redis.ttl key
44
44
  end
45
45
 
46
+ def serialize(value)
47
+ type = options[:type]
48
+
49
+ if type.nil? || value.nil?
50
+ value
51
+ else
52
+ value.kind_of?(Enumerable) ? value.map(&:to_json) : value.to_json
53
+ end
54
+ end
55
+
56
+ def desserialize(value)
57
+ type = options[:type]
58
+
59
+ if type.nil? || value.nil?
60
+ value
61
+ else
62
+ value.kind_of?(Enumerable) ? value.map { |v| type.from_json(v) }
63
+ : type.from_json(value)
64
+ end
65
+ end
66
+
46
67
  end
47
68
  end
48
69
  end
@@ -33,11 +33,11 @@ module Hari
33
33
  end
34
34
 
35
35
  def []=(index, member)
36
- Hari.redis.lset key, index, member
36
+ Hari.redis.lset key, index, serialize(member)
37
37
  end
38
38
 
39
39
  def range(start = 0, stop = -1)
40
- Hari.redis.lrange key, start, stop
40
+ desserialize Hari.redis.lrange(key, start, stop)
41
41
  end
42
42
 
43
43
  alias :members :range
@@ -52,7 +52,7 @@ module Hari
52
52
  end
53
53
 
54
54
  def at(index)
55
- Hari.redis.lindex key, index
55
+ desserialize Hari.redis.lindex(key, index)
56
56
  end
57
57
 
58
58
  alias :index :at
@@ -81,20 +81,22 @@ module Hari
81
81
  end
82
82
 
83
83
  def include?(member)
84
- range.include? member
84
+ range.include? serialize(member)
85
85
  end
86
86
 
87
87
  alias :member? :include?
88
88
 
89
89
  def push(*members)
90
- Hari.redis.rpush key, members
90
+ return if Array(members).empty?
91
+
92
+ Hari.redis.rpush key, serialize(members)
91
93
  end
92
94
 
93
95
  alias :rpush :push
94
96
  alias :add :push
95
97
 
96
98
  def lpush(*members)
97
- Hari.redis.lpush key, members
99
+ Hari.redis.lpush key, serialize(members)
98
100
  end
99
101
 
100
102
  def <<(member)
@@ -116,13 +118,13 @@ module Hari
116
118
  end
117
119
 
118
120
  def pop
119
- Hari.redis.rpop key
121
+ desserialize Hari.redis.rpop(key)
120
122
  end
121
123
 
122
124
  alias :rpop :pop
123
125
 
124
126
  def shift
125
- Hari.redis.lpop key
127
+ desserialize Hari.redis.lpop(key)
126
128
  end
127
129
 
128
130
  alias :lpop :shift
@@ -13,11 +13,11 @@ module Hari
13
13
  end
14
14
 
15
15
  def members
16
- Hari.redis.smembers key
16
+ desserialize Hari.redis.smembers(key)
17
17
  end
18
18
 
19
19
  def rand(count = 1)
20
- Hari.redis.srandmember key, count
20
+ desserialize Hari.redis.srandmember(key, count)
21
21
  end
22
22
 
23
23
  def count
@@ -40,13 +40,13 @@ module Hari
40
40
  end
41
41
 
42
42
  def include?(member)
43
- Hari.redis.sismember key, member
43
+ Hari.redis.sismember key, serialize(member)
44
44
  end
45
45
 
46
46
  alias :member? :include?
47
47
 
48
48
  def add(*members)
49
- Hari.redis.sadd key, members
49
+ Hari.redis.sadd key, serialize(members)
50
50
  end
51
51
 
52
52
  def <<(member)
@@ -54,15 +54,15 @@ module Hari
54
54
  end
55
55
 
56
56
  def delete(*members)
57
- Hari.redis.srem key, members
57
+ Hari.redis.srem key, serialize(members)
58
58
  end
59
59
 
60
60
  def pop
61
- Hari.redis.spop key
61
+ desserialize Hari.redis.spop(key)
62
62
  end
63
63
 
64
64
  def intersect(*set_queries)
65
- Hari.redis.sinter key, set_query_keys(set_queries)
65
+ desserialize Hari.redis.sinter(key, set_query_keys(set_queries))
66
66
  end
67
67
 
68
68
  def &(other_set_query)
@@ -70,7 +70,7 @@ module Hari
70
70
  end
71
71
 
72
72
  def diff(*set_queries)
73
- Hari.redis.sdiff key, set_query_keys(set_queries)
73
+ desserialize Hari.redis.sdiff(key, set_query_keys(set_queries))
74
74
  end
75
75
 
76
76
  def -(other_set_query)
@@ -15,7 +15,10 @@ module Hari
15
15
  def range(start = 0, stop = -1, options = {})
16
16
  return revrange(start, stop, options) if options[:desc]
17
17
 
18
- Hari.redis.zrange key, start, stop, options.slice(:with_scores)
18
+ result = Hari.redis.zrange key, start, stop,
19
+ options.slice(:with_scores)
20
+
21
+ desserialize result
19
22
  end
20
23
 
21
24
  alias :members :range
@@ -25,7 +28,10 @@ module Hari
25
28
  end
26
29
 
27
30
  def revrange(start = 0, stop = -1, options = {})
28
- Hari.redis.zrevrange key, start, stop, options.slice(:with_scores)
31
+ result = Hari.redis.zrevrange key, start, stop,
32
+ options.slice(:with_scores)
33
+
34
+ desserialize result
29
35
  end
30
36
 
31
37
  alias :reverse_range :revrange
@@ -38,11 +44,17 @@ module Hari
38
44
  def range_by_score(min, max, options = {})
39
45
  return revrange_by_score(min, max, options) if options[:desc]
40
46
 
41
- Hari.redis.zrangebyscore key, min, max, options.slice(:with_scores, :limit)
47
+ result = Hari.redis.zrangebyscore key, min, max,
48
+ options.slice(:with_scores, :limit)
49
+
50
+ desserialize result
42
51
  end
43
52
 
44
53
  def revrange_by_score(min, max, options = {})
45
- Hari.redis.zrevrangebyscore key, max, min, options.slice(:with_scores, :limit)
54
+ result = Hari.redis.zrevrangebyscore key, max, min,
55
+ options.slice(:with_scores, :limit)
56
+
57
+ desserialize result
46
58
  end
47
59
 
48
60
  def rank(member, options = {})
@@ -55,7 +67,7 @@ module Hari
55
67
  alias :position :rank
56
68
 
57
69
  def revrank(member)
58
- Hari.redis.zrevrank key, member
70
+ Hari.redis.zrevrank key, serialize(member)
59
71
  end
60
72
 
61
73
  alias :reverse_ranking :revrank
@@ -87,11 +99,11 @@ module Hari
87
99
  alias :member? :include?
88
100
 
89
101
  def score(member)
90
- Hari.redis.zscore key, member
102
+ Hari.redis.zscore key, serialize(member)
91
103
  end
92
104
 
93
105
  def add(*score_members)
94
- Hari.redis.zadd key, score_members.to_a.flatten
106
+ Hari.redis.zadd key, serialize(score_members.to_a.flatten)
95
107
  end
96
108
 
97
109
  def <<(*score_members)
@@ -99,7 +111,7 @@ module Hari
99
111
  end
100
112
 
101
113
  def delete(*members)
102
- Hari.redis.zrem key, members
114
+ Hari.redis.zrem key, serialize(members)
103
115
  end
104
116
 
105
117
  def trim_by_rank(start, stop)
@@ -1,6 +1,7 @@
1
1
  require 'hari/node/repository'
2
2
  require 'hari/node/queries'
3
3
  require 'hari/node/serialization'
4
+ require 'hari/node/index'
4
5
 
5
6
  module Hari
6
7
  class Node < Entity
@@ -15,10 +16,22 @@ module Hari
15
16
  super
16
17
  end
17
18
 
19
+ after_update { reindex }
20
+ after_create { reindex force_index: true }
21
+ after_destroy { remove_from_indexes }
22
+
18
23
  def generate_id
19
- return super unless model_id
24
+ unless model_id.present?
25
+ begin
26
+ self.model_id = SecureRandom.hex(8)
27
+ end until !Hari.redis.exists(node_key)
28
+ end
29
+
30
+ node_key
31
+ end
20
32
 
21
- model_id.to_s.include?('#') ? model_id.to_s : "#{node_type}##{model_id}"
33
+ def node_key
34
+ "#{node_type}##{model_id}"
22
35
  end
23
36
 
24
37
  def node_type
@@ -29,5 +42,9 @@ module Hari
29
42
  self.to_s.underscore
30
43
  end
31
44
 
45
+ def self.indexed_properties
46
+ properties.select { |p| p.options[:index] }
47
+ end
48
+
32
49
  end
33
50
  end
@@ -0,0 +1,152 @@
1
+ module Hari
2
+ class Node
3
+ class Index
4
+
5
+ attr_accessor :property, :value, :indexes, :options
6
+
7
+ attr_writer :start, :stop
8
+
9
+ def initialize(property, value)
10
+ @property, @value = property, value
11
+ @indexes, @options = [], {}
12
+ end
13
+
14
+ def add(node)
15
+ Hari.redis.zadd key, Time.now.to_f, node.model_id
16
+ end
17
+
18
+ def delete(node)
19
+ Hari.redis.zrem key, node.model_id
20
+ end
21
+
22
+ def append(index)
23
+ self.indexes << index
24
+
25
+ self
26
+ end
27
+
28
+ def start
29
+ @start ||= 0
30
+ end
31
+
32
+ def stop
33
+ @stop ||= -1
34
+ end
35
+
36
+ def limit(page = nil, per_page = nil)
37
+ if page.present? && per_page.present?
38
+ self.start = page * per_page
39
+ self.stop = start + per_page - 1
40
+ end
41
+
42
+ self
43
+ end
44
+
45
+ def from(score, direction = nil)
46
+ direction ||= :up
47
+ options[:from] = { score: score.to_f, direction: direction.to_s }
48
+
49
+ self
50
+ end
51
+
52
+ def count
53
+ return count_intersect unless indexes.empty?
54
+
55
+ from = options[:from]
56
+ limit = stop == -1 ? stop : stop - start + 1
57
+
58
+ if from.present? && from[:direction] == 'up'
59
+ Hari.redis.zrevrangebyscore(key, '+inf', from[:score]).size
60
+ elsif from.present? && from[:direction] == 'down'
61
+ Hari.redis.zrevrangebyscore(key, from[:score], '-inf').size
62
+ else
63
+ Hari.redis.zcard key
64
+ end
65
+ end
66
+
67
+ def count_intersect
68
+ intersect
69
+
70
+ from = options[:from]
71
+ limit = stop == -1 ? stop : stop - start + 1
72
+
73
+ count = if from.present? && from[:direction] == 'up'
74
+ Hari.redis.zrevrangebyscore(intersect_key, '+inf', from[:score]).size
75
+ elsif from.present? && from[:direction] == 'down'
76
+ Hari.redis.zrevrangebyscore(intersect_key, from[:score], '-inf').size
77
+ else
78
+ Hari.redis.zcard intersect_key
79
+ end
80
+
81
+ Hari.redis.del intersect_key
82
+
83
+ count
84
+ end
85
+
86
+ def list
87
+ return list_intersect unless indexes.empty?
88
+
89
+ from = options[:from]
90
+ limit = stop == -1 ? stop : stop - start + 1
91
+
92
+ ids = if from.present? && from[:direction] == 'up'
93
+ Hari.redis.zrevrangebyscore key, '+inf', from[:score], limit: [start, limit]
94
+ elsif from.present? && from[:direction] == 'down'
95
+ Hari.redis.zrevrangebyscore key, from[:score], '-inf', limit: [start, limit]
96
+ else
97
+ Hari.redis.zrevrange key, start, stop
98
+ end
99
+
100
+ property.entity.find_many ids
101
+ end
102
+
103
+ def list_intersect
104
+ intersect
105
+
106
+ from = options[:from]
107
+ limit = stop == -1 ? stop : stop - start + 1
108
+
109
+ ids = if from.present? && from[:direction] == 'up'
110
+ Hari.redis.zrevrangebyscore intersect_key, '+inf', from[:score], limit: [start, limit]
111
+ elsif from.present? && from[:direction] == 'down'
112
+ Hari.redis.zrevrangebyscore intersect_key, from[:score], '-inf', limit: [start, limit]
113
+ else
114
+ Hari.redis.zrevrange intersect_key, start, stop
115
+ end
116
+
117
+ Hari.redis.del intersect_key
118
+
119
+ property.entity.find_many ids
120
+ end
121
+
122
+ alias :to_a :list
123
+ alias :result :list
124
+
125
+ def key
126
+ "#{property.entity.node_type}|#{property.name}:#{digest(value)}"
127
+ end
128
+
129
+ private
130
+
131
+ def digest(value)
132
+ case value = value.to_s.strip
133
+ when ''
134
+ '_NULL_'
135
+ when /^[[:alnum:]]+$/
136
+ value
137
+ else
138
+ Digest::MD5.hexdigest value
139
+ end
140
+ end
141
+
142
+ def intersect
143
+ Hari.redis.zinterstore intersect_key, [key] + indexes.map(&:key), aggregate: 'max'
144
+ end
145
+
146
+ def intersect_key
147
+ "inter:#{key}:#{indexes.map(&:key).join(':')}"
148
+ end
149
+
150
+ end
151
+ end
152
+ end
@@ -1,34 +1,49 @@
1
+ require 'hari/node/queries/relation'
2
+ require 'hari/node/queries/type'
3
+
1
4
  module Hari
2
5
  class Node < Entity
3
6
  module Queries
4
- autoload :Relation, 'hari/node/queries/relation'
5
- autoload :Type, 'hari/node/queries/type'
7
+ extend ActiveSupport::Concern
6
8
 
7
9
  delegate :in, :out, to: :relation_query
8
10
 
9
- delegate :set, :set!, to: :set_query
10
- delegate :sorted_set, :sorted_set!, to: :sorted_set_query
11
- delegate :list, :list!, to: :list_query
12
- delegate :string, :string!, to: :string_query
11
+ Keys::TYPES.each do |key|
12
+ query_builder = Keys.const_get(key.camelize)
13
13
 
14
- private
14
+ define_method key do |name = nil, options = {}|
15
+ return super() unless name
15
16
 
16
- def set_query
17
- Keys::Set.new query_node
18
- end
17
+ query = query_builder.new(query_node, options)
18
+ query.send key, name
19
+ end
19
20
 
20
- def sorted_set_query
21
- Keys::SortedSet.new query_node
21
+ define_method "#{key}!" do |name, options = {}|
22
+ query = query_builder.new(query_node, options)
23
+ query.send "#{key}!", name
24
+ end
22
25
  end
23
26
 
24
- def list_query
25
- Keys::List.new query_node
26
- end
27
+ included do
28
+ Keys::TYPES.each do |key|
29
+ define_singleton_method key do |name = nil, options = {}|
30
+ return super() unless name
27
31
 
28
- def string_query
29
- Keys::String.new query_node
32
+ define_method(name) { send key, name, options }
33
+ define_method("#{name}!") { send "#{key}!", name, options }
34
+
35
+ define_method "#{name}=" do |value|
36
+ data = send(name)
37
+ data.delete!
38
+
39
+ data.add *value
40
+ end
41
+ end
42
+ end
30
43
  end
31
44
 
45
+ private
46
+
32
47
  def relation_query
33
48
  Queries::Relation::Start.new query_node
34
49
  end