hari 0.0.1 → 0.0.3

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 (43) hide show
  1. checksums.yaml +15 -0
  2. data/.travis.yml +15 -0
  3. data/LICENSE +15 -19
  4. data/README.md +22 -0
  5. data/hari.gemspec +2 -1
  6. data/lib/hari.rb +56 -1
  7. data/lib/hari/entity.rb +8 -11
  8. data/lib/hari/entity/property.rb +2 -2
  9. data/lib/hari/entity/property/builder.rb +9 -1
  10. data/lib/hari/entity/repository.rb +3 -2
  11. data/lib/hari/entity/serialization/float.rb +2 -0
  12. data/lib/hari/entity/serialization/integer.rb +3 -10
  13. data/lib/hari/node.rb +20 -3
  14. data/lib/hari/node/queries.rb +33 -0
  15. data/lib/hari/node/queries/list.rb +147 -0
  16. data/lib/hari/node/queries/relation.rb +100 -0
  17. data/lib/hari/node/queries/relation/backend/list.rb +35 -0
  18. data/lib/hari/node/queries/relation/backend/sorted_set.rb +95 -0
  19. data/lib/hari/node/queries/relation/runnable.rb +24 -0
  20. data/lib/hari/node/queries/relation/start.rb +19 -0
  21. data/lib/hari/node/queries/relation/step.rb +17 -0
  22. data/lib/hari/node/queries/set.rb +116 -0
  23. data/lib/hari/node/queries/sorted_set.rb +130 -0
  24. data/lib/hari/node/repository.rb +24 -0
  25. data/lib/hari/node/serialization.rb +24 -0
  26. data/lib/hari/relation.rb +72 -0
  27. data/lib/hari/relation/linked_list.rb +16 -0
  28. data/lib/hari/relation/sorted_set.rb +16 -0
  29. data/lib/hari/version.rb +1 -1
  30. data/spec/hari/configuration_spec.rb +5 -0
  31. data/spec/hari/entity/repository_spec.rb +4 -4
  32. data/spec/hari/entity/serialization_spec.rb +2 -2
  33. data/spec/hari/node/lists_spec.rb +126 -0
  34. data/spec/hari/node/sets_spec.rb +85 -0
  35. data/spec/hari/node/sorted_sets_spec.rb +89 -0
  36. data/spec/hari/node_spec.rb +79 -0
  37. data/spec/hari_spec.rb +37 -0
  38. data/spec/spec_helper.rb +11 -1
  39. metadata +48 -32
  40. data/lib/hari/relationship.rb +0 -72
  41. data/lib/hari/relationship/linked_list.rb +0 -22
  42. data/lib/hari/relationship/sorted_set.rb +0 -22
  43. data/lib/hari/scripts.rb +0 -26
@@ -0,0 +1,100 @@
1
+ require 'hari/node/queries/relation/step'
2
+ require 'hari/node/queries/relation/start'
3
+ require 'hari/node/queries/relation/runnable'
4
+ require 'hari/node/queries/relation/backend/sorted_set'
5
+
6
+ module Hari
7
+ class Node < Entity
8
+ module Queries
9
+ class Relation
10
+ include Relation::Step
11
+ include Relation::Runnable
12
+
13
+ attr_reader :parent, :direction, :relation, :level, :options
14
+
15
+ def initialize(parent, direction, relation, *args)
16
+ @parent, @direction, @relation = parent, direction, relation
17
+ @level = parent.level + 1
18
+ @options = {}
19
+ args.extract_options!.each { |k, v| send k, v }
20
+
21
+ @options[:backend] = args.first.presence || Backend::SortedSet
22
+ end
23
+
24
+ def backend
25
+ options[:backend]
26
+ end
27
+
28
+ def calculate_limit
29
+ options[:limit] ? (options[:limit].to_i - 1) : -1
30
+ end
31
+
32
+ %w(limit from step).each do |method|
33
+ define_method method do |value|
34
+ options[method.to_sym] = value
35
+ self
36
+ end
37
+ end
38
+
39
+ # TODO for later, filter by node type
40
+ def types(*types)
41
+ options[:types] = types
42
+ self
43
+ end
44
+
45
+ alias :type :types
46
+
47
+ %w(nodes_ids relations_ids nodes).each do |result_type|
48
+ define_method result_type do
49
+ options[:result_type] = result_type.to_sym
50
+ self
51
+ end
52
+
53
+ define_method "#{result_type}!" do
54
+ send result_type
55
+ result
56
+ end
57
+ end
58
+
59
+ alias :nids :nodes_ids
60
+ alias :rel_ids :relations_ids
61
+ alias :rids :relations_ids
62
+
63
+ def count
64
+ options[:result_type] = :count
65
+ result
66
+ end
67
+
68
+ def start_node
69
+ level == 1 ? parent.node : parent.start_node
70
+ end
71
+
72
+ def call(final = true)
73
+ if level == 1
74
+ backend.fetch parent.node, call_args(final)
75
+ else
76
+ backend.step start_node, parent.call(false), call_args(final)
77
+ end
78
+ end
79
+
80
+ def call_args(final = true)
81
+ {
82
+ relation: relation,
83
+ direction: direction,
84
+ limit: calculate_limit,
85
+ from: options[:from],
86
+ step: options[:step],
87
+ result: result_type(final)
88
+ }
89
+ end
90
+
91
+ def result_type(final = false)
92
+ return :nodes_ids unless final
93
+
94
+ options.fetch :result_type, :nodes
95
+ end
96
+
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,35 @@
1
+ module Hari::Node::Queries::Relation
2
+ module Backend
3
+ module List
4
+ extend self
5
+
6
+ def fetch(node, options = {})
7
+ list = node.list list_name(options)
8
+ send "fetch_#{options[:result]}", list, options
9
+ end
10
+
11
+ def fetch_relations_ids(list, options = {})
12
+ start = options.fetch(:from, 0)
13
+ stop = options.limit(:limit, -1)
14
+
15
+ list.range start, stop
16
+ end
17
+
18
+ def fetch_nodes_ids(list, options)
19
+ index = list.name =~ /in$/ ? 1 : 2
20
+ fetch_relations_ids(list, options).map { |r| r.split(':')[index] }
21
+ end
22
+
23
+ def fetch_count(list, options)
24
+ list.count
25
+ end
26
+
27
+ private
28
+
29
+ def list_name(options)
30
+ "#{options[:relation]}:#{options[:direction]}"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,95 @@
1
+ class Hari::Node::Queries::Relation
2
+ module Backend
3
+ module SortedSet
4
+ extend self
5
+
6
+ def fetch(node, options = {})
7
+ set = node.sorted_set set_name(options)
8
+ send "fetch_#{options[:result]}", set, options
9
+ end
10
+
11
+ def fetch_relations_ids(set, options)
12
+ from, limit = options.values_at(:from, :limit)
13
+
14
+ if from.present?
15
+ set.range_by_score from, '+inf', desc: true, limit: [0, limit]
16
+ else
17
+ set.range from, limit, desc: true
18
+ end
19
+ end
20
+
21
+ def fetch_nodes_ids(set, options)
22
+ index = set.name =~ /in$/ ? 0 : 2
23
+ fetch_relations_ids(set, options).map { |r| r.split(':')[index] }
24
+ end
25
+
26
+ def fetch_nodes(set, options)
27
+ nodes_ids = fetch_nodes_ids(set, options)
28
+ nodes_ids.empty? ? [] : Hari.redis.mget(nodes_ids)
29
+ end
30
+
31
+ def fetch_count(set, options)
32
+ set.count
33
+ end
34
+
35
+ def step(start_node, nodes_ids, options = {})
36
+ stream = start_node.sorted_set("stream:#{SecureRandom.hex(6)}")
37
+ direction = options[:direction] == :in ? 0 : 2
38
+ limit = options.fetch(:limit, -1)
39
+
40
+ nodes_ids.each_with_index do |node_id, index|
41
+ prune, stop = true, options.fetch(:step, 5)
42
+
43
+ if limit == -1 || stream.count < limit
44
+ prune, stop = false, limit
45
+ end
46
+
47
+ start, digg = 0, true
48
+
49
+ while digg
50
+ set = Hari(node_id).sorted_set set_name(options)
51
+
52
+ if from = options[:from].presence
53
+ scored_relations_ids = set.range_by_score(from, '+inf', desc: true, with_scores: true, limit: [start, stop])
54
+ else
55
+ scored_relations_ids = set.range(start, stop, desc: true, with_scores: true)
56
+ end
57
+
58
+ break if scored_relations_ids.empty?
59
+
60
+ scored_nodes_ids = scored_relations_ids.map { |(r, s)| [s, r.split(':')[direction]] }.flatten
61
+ stream.add *scored_nodes_ids
62
+
63
+ last_node_id = scored_nodes_ids.last
64
+
65
+ if prune
66
+ stream.trim_by_rank 0, stop
67
+
68
+ unless stream.include? last_node_id
69
+ digg = false
70
+ start += stop + 1
71
+ end
72
+ else
73
+ digg = false
74
+ end
75
+ end
76
+ end
77
+
78
+ nodes_ids = stream.revrange
79
+
80
+ if nodes_ids.any? && options[:result] == :nodes
81
+ Hari.redis.mget nodes_ids
82
+ else
83
+ nodes_ids
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def set_name(options)
90
+ "#{options[:relation]}:#{options[:direction]}"
91
+ end
92
+
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,24 @@
1
+ module Hari
2
+ class Node < Entity
3
+ module Queries
4
+ class Relation
5
+ module Runnable
6
+
7
+ def result
8
+ result = call(true)
9
+
10
+ case result_type(true)
11
+ when :nodes
12
+ result.map &Hari::Node.method(:from_source)
13
+ else
14
+ result
15
+ end
16
+ end
17
+
18
+ alias :to_a :result
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module Hari
2
+ class Node < Entity
3
+ module Queries
4
+ class Relation
5
+ class Start
6
+ include Relation::Step
7
+
8
+ attr_reader :node, :level
9
+
10
+ def initialize(node)
11
+ @node = node
12
+ @level = 0
13
+ end
14
+
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ module Hari
2
+ class Node < Entity
3
+ module Queries
4
+ class Relation
5
+ module Step
6
+
7
+ %w(in out).each do |direction|
8
+ define_method direction do |*args|
9
+ Relation.new self, direction, *args
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,116 @@
1
+ module Hari
2
+ class Node < Entity
3
+ module Queries
4
+ class Set
5
+
6
+ attr_reader :node, :name
7
+
8
+ def initialize(node = nil)
9
+ @node = node
10
+ end
11
+
12
+ def key
13
+ @key ||= begin
14
+ prefix = node ? "#{Hari.node_key(node)}:" : ''
15
+ prefix + name.to_s
16
+ end
17
+ end
18
+
19
+ def set(name)
20
+ @name = name
21
+ self
22
+ end
23
+
24
+ def set!(name)
25
+ @name = name
26
+ members
27
+ end
28
+
29
+ def members
30
+ Hari.redis.smembers key
31
+ end
32
+
33
+ def rand(count = 1)
34
+ Hari.redis.srandmember key, count
35
+ end
36
+
37
+ def count
38
+ Hari.redis.scard key
39
+ end
40
+
41
+ alias :size :count
42
+ alias :length :count
43
+
44
+ def empty?
45
+ count == 0
46
+ end
47
+
48
+ def one?
49
+ count == 1
50
+ end
51
+
52
+ def many?
53
+ count > 1
54
+ end
55
+
56
+ def include?(member)
57
+ Hari.redis.sismember key, member
58
+ end
59
+
60
+ alias :member? :include?
61
+
62
+ def add(*members)
63
+ Hari.redis.sadd key, members
64
+ end
65
+
66
+ def <<(member)
67
+ add member
68
+ end
69
+
70
+ def delete(*members)
71
+ Hari.redis.srem key, members
72
+ end
73
+
74
+ def pop
75
+ Hari.redis.spop key
76
+ end
77
+
78
+ def intersect(*set_queries)
79
+ Hari.redis.sinter key, set_query_keys(set_queries)
80
+ end
81
+
82
+ def &(other_set_query)
83
+ intersect other_set_query
84
+ end
85
+
86
+ def diff(*set_queries)
87
+ Hari.redis.sdiff key, set_query_keys(set_queries)
88
+ end
89
+
90
+ def -(other_set_query)
91
+ diff other_set_query
92
+ end
93
+
94
+ private
95
+
96
+ def set_query_keys(set_queries)
97
+ keys = set_queries.map do |query|
98
+ ensure_set_query! query
99
+ query.key
100
+ end
101
+
102
+ fail 'no query keys' if keys.empty?
103
+
104
+ keys
105
+ end
106
+
107
+ def ensure_set_query!(query)
108
+ unless query.kind_of?(Hari::Node::Queries::Set)
109
+ fail 'not a set query'
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,130 @@
1
+ module Hari
2
+ class Node < Entity
3
+ module Queries
4
+ class SortedSet
5
+
6
+ attr_reader :node, :name
7
+
8
+ def initialize(node = nil)
9
+ @node = node
10
+ end
11
+
12
+ def key
13
+ @key ||= begin
14
+ prefix = node ? "#{Hari.node_key(node)}:" : ''
15
+ prefix + name.to_s
16
+ end
17
+ end
18
+
19
+ def sorted_set(name)
20
+ @name = name
21
+ self
22
+ end
23
+
24
+ def sorted_set!(name)
25
+ @name = name
26
+ members
27
+ end
28
+
29
+ def range(start = 0, stop = -1, options = {})
30
+ return revrange(start, stop, options) if options[:desc]
31
+
32
+ Hari.redis.zrange key, start, stop, options.slice(:with_scores)
33
+ end
34
+
35
+ alias :members :range
36
+
37
+ def range_with_scores
38
+ range 0, -1, with_scores: true
39
+ end
40
+
41
+ def revrange(start = 0, stop = -1, options = {})
42
+ Hari.redis.zrevrange key, start, stop, options.slice(:with_scores)
43
+ end
44
+
45
+ alias :reverse_range :revrange
46
+ alias :desc_range :revrange
47
+
48
+ def revrange_with_scores
49
+ revrange 0, -1, with_scores: true
50
+ end
51
+
52
+ def range_by_score(min, max, options = {})
53
+ return revrange_by_score(min, max, options) if options[:desc]
54
+
55
+ Hari.redis.zrangebyscore key, min, max, options.slice(:with_scores, :limit)
56
+ end
57
+
58
+ def revrange_by_score(min, max, options = {})
59
+ Hari.redis.zrevrangebyscore key, max, min, options.slice(:with_scores, :limit)
60
+ end
61
+
62
+ def rank(member, options = {})
63
+ return revrank(member, options) if options[:desc]
64
+
65
+ Hari.redis.zrank key, member
66
+ end
67
+
68
+ alias :ranking :rank
69
+ alias :position :rank
70
+
71
+ def revrank(member)
72
+ Hari.redis.zrevrank key, member
73
+ end
74
+
75
+ alias :reverse_ranking :revrank
76
+ alias :reverse_position :revrank
77
+
78
+ def count
79
+ Hari.redis.zcard key
80
+ end
81
+
82
+ alias :size :count
83
+ alias :length :count
84
+
85
+ def empty?
86
+ count == 0
87
+ end
88
+
89
+ def one?
90
+ count == 1
91
+ end
92
+
93
+ def many?
94
+ count > 1
95
+ end
96
+
97
+ def include?(member)
98
+ score(member).present?
99
+ end
100
+
101
+ alias :member? :include?
102
+
103
+ def score(member)
104
+ Hari.redis.zscore key, member
105
+ end
106
+
107
+ def add(*score_members)
108
+ Hari.redis.zadd key, score_members.to_a.flatten
109
+ end
110
+
111
+ def <<(*score_members)
112
+ add score_members
113
+ end
114
+
115
+ def delete(*members)
116
+ Hari.redis.zrem key, members
117
+ end
118
+
119
+ def trim_by_rank(start, stop)
120
+ Hari.redis.zremrangebyrank key, start, stop
121
+ end
122
+
123
+ def trim_by_score(min, max)
124
+ Hari.redis.zremrangebyscore key, min, max
125
+ end
126
+
127
+ end
128
+ end
129
+ end
130
+ end