ohm 1.0.0.rc3 → 1.0.0.rc4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ohm.rb +67 -23
- data/test/filtering.rb +69 -0
- metadata +31 -11
data/lib/ohm.rb
CHANGED
@@ -423,10 +423,9 @@ module Ohm
|
|
423
423
|
# set.find(:age => 30)
|
424
424
|
#
|
425
425
|
def find(dict)
|
426
|
-
|
427
|
-
keys.push(key)
|
426
|
+
filters = model.filters(dict).push(key)
|
428
427
|
|
429
|
-
MultiSet.new(
|
428
|
+
MultiSet.new(namespace, model).append(:sinterstore, filters)
|
430
429
|
end
|
431
430
|
|
432
431
|
# Reduce the set using any number of filters.
|
@@ -440,7 +439,7 @@ module Ohm
|
|
440
439
|
# User.find(:name => "John").except(:country => "US")
|
441
440
|
#
|
442
441
|
def except(dict)
|
443
|
-
MultiSet.new(
|
442
|
+
MultiSet.new(namespace, model).append(:sinterstore, key).except(dict)
|
444
443
|
end
|
445
444
|
|
446
445
|
# Do a union to the existing set using any number of filters.
|
@@ -454,7 +453,7 @@ module Ohm
|
|
454
453
|
# User.find(:name => "John").union(:name => "Jane")
|
455
454
|
#
|
456
455
|
def union(dict)
|
457
|
-
MultiSet.new(
|
456
|
+
MultiSet.new(namespace, model).append(:sinterstore, key).union(dict)
|
458
457
|
end
|
459
458
|
|
460
459
|
private
|
@@ -516,7 +515,6 @@ module Ohm
|
|
516
515
|
end
|
517
516
|
end
|
518
517
|
|
519
|
-
|
520
518
|
# Anytime you filter a set with more than one requirement, you
|
521
519
|
# internally use a `MultiSet`. `MutiSet` is a bit slower than just
|
522
520
|
# a `Set` because it has to `SINTERSTORE` all the keys prior to
|
@@ -533,9 +531,15 @@ module Ohm
|
|
533
531
|
# User.find(:name => "John", :age => 30).kind_of?(Ohm::MultiSet)
|
534
532
|
# # => true
|
535
533
|
#
|
536
|
-
class MultiSet < Struct.new(:
|
534
|
+
class MultiSet < Struct.new(:namespace, :model)
|
537
535
|
include Collection
|
538
536
|
|
537
|
+
def append(operation, list)
|
538
|
+
filters.push([operation, list])
|
539
|
+
|
540
|
+
return self
|
541
|
+
end
|
542
|
+
|
539
543
|
# Chain new fiters on an existing set.
|
540
544
|
#
|
541
545
|
# Example:
|
@@ -544,10 +548,9 @@ module Ohm
|
|
544
548
|
# set.find(:status => 'pending')
|
545
549
|
#
|
546
550
|
def find(dict)
|
547
|
-
|
548
|
-
keys.push(*self.keys)
|
551
|
+
filters.push([:sinterstore, model.filters(dict)])
|
549
552
|
|
550
|
-
|
553
|
+
return self
|
551
554
|
end
|
552
555
|
|
553
556
|
# Reduce the set using any number of filters.
|
@@ -561,7 +564,7 @@ module Ohm
|
|
561
564
|
# User.find(:name => "John").except(:country => "US")
|
562
565
|
#
|
563
566
|
def except(dict)
|
564
|
-
|
567
|
+
filters.push([:sdiffstore, model.filters(dict)])
|
565
568
|
|
566
569
|
return self
|
567
570
|
end
|
@@ -577,30 +580,71 @@ module Ohm
|
|
577
580
|
# User.find(:name => "John").union(:name => "Jane")
|
578
581
|
#
|
579
582
|
def union(dict)
|
580
|
-
|
583
|
+
filters.push([:sunionstore, model.filters(dict)])
|
581
584
|
|
582
585
|
return self
|
583
586
|
end
|
584
587
|
|
585
588
|
private
|
586
|
-
def
|
587
|
-
@
|
589
|
+
def filters
|
590
|
+
@filters ||= []
|
588
591
|
end
|
589
592
|
|
590
|
-
def
|
591
|
-
@
|
593
|
+
def temp_keys
|
594
|
+
@temp_keys ||= []
|
592
595
|
end
|
593
596
|
|
594
|
-
def
|
597
|
+
def clean_temp_keys
|
598
|
+
model.db.del(*temp_keys)
|
599
|
+
temp_keys.clear
|
600
|
+
end
|
601
|
+
|
602
|
+
def generate_temp_key
|
595
603
|
key = namespace[:temp][SecureRandom.hex(32)]
|
596
|
-
key
|
597
|
-
key
|
598
|
-
|
604
|
+
temp_keys << key
|
605
|
+
key
|
606
|
+
end
|
607
|
+
|
608
|
+
def execute
|
609
|
+
|
610
|
+
# Hold the final result key for this MultiSet.
|
611
|
+
main = nil
|
612
|
+
|
613
|
+
filters.each do |operation, list|
|
614
|
+
|
615
|
+
# Operation can be sinterstore, sdiffstore, or sunionstore.
|
616
|
+
# each operation we do, i.e. `.union(...)`, will be considered
|
617
|
+
# one intersected set, hence we need to `sinterstore` all
|
618
|
+
# the filters in a temporary set.
|
619
|
+
temp = generate_temp_key
|
620
|
+
temp.sinterstore(*list)
|
621
|
+
|
622
|
+
# If this is the first set, we simply assign the generated
|
623
|
+
# set to main, which could possibly be the return value
|
624
|
+
# for simple filters like one `.find(...)`.
|
625
|
+
if main.nil?
|
626
|
+
main = temp
|
627
|
+
else
|
628
|
+
|
629
|
+
# Append the generated temporary set using the operation.
|
630
|
+
# i.e. if we have (mood=happy & book=1) and we have an
|
631
|
+
# `sunionstore`, we do (mood=happy & book=1) | (mood=sad & book=1)
|
632
|
+
main.send(operation, main, temp)
|
633
|
+
end
|
634
|
+
end
|
599
635
|
|
600
636
|
begin
|
601
|
-
|
637
|
+
|
638
|
+
# At this point, we have the final aggregated set, which we yield
|
639
|
+
# to the caller. the caller can do all the normal set operations,
|
640
|
+
# i.e. SCARD, SMEMBERS, etc.
|
641
|
+
yield main
|
642
|
+
|
602
643
|
ensure
|
603
|
-
|
644
|
+
|
645
|
+
# We have to make sure we clean up the temporary keys to avoid
|
646
|
+
# memory leaks and the unintended explosion of memory usage.
|
647
|
+
clean_temp_keys
|
604
648
|
end
|
605
649
|
end
|
606
650
|
end
|
@@ -789,7 +833,7 @@ module Ohm
|
|
789
833
|
if keys.size == 1
|
790
834
|
Ohm::Set.new(keys.first, key, self)
|
791
835
|
else
|
792
|
-
Ohm::MultiSet.new(
|
836
|
+
Ohm::MultiSet.new(key, self).append(:sinterstore, keys)
|
793
837
|
end
|
794
838
|
end
|
795
839
|
|
data/test/filtering.rb
CHANGED
@@ -84,4 +84,73 @@ test "#union" do |john, jane|
|
|
84
84
|
assert res.include?(john)
|
85
85
|
assert res.include?(jane)
|
86
86
|
assert res.include?(included)
|
87
|
+
|
88
|
+
res = User.find(:status => "active").union(:status => "inactive").find(:lname => "Doe")
|
89
|
+
|
90
|
+
assert res.any? { |e| e.status == "inactive" }
|
91
|
+
end
|
92
|
+
|
93
|
+
# book author thing via @myobie
|
94
|
+
scope do
|
95
|
+
class Book < Ohm::Model
|
96
|
+
collection :authors, :Author
|
97
|
+
end
|
98
|
+
|
99
|
+
class Author < Ohm::Model
|
100
|
+
reference :book, :Book
|
101
|
+
|
102
|
+
attribute :mood
|
103
|
+
index :mood
|
104
|
+
end
|
105
|
+
|
106
|
+
setup do
|
107
|
+
book1 = Book.create
|
108
|
+
book2 = Book.create
|
109
|
+
|
110
|
+
auth1 = Author.create(:book => book1, :mood => "happy")
|
111
|
+
auth2 = Author.create(:book => book1, :mood => "sad")
|
112
|
+
auth3 = Author.create(:book => book2, :mood => "sad")
|
113
|
+
|
114
|
+
[book1, book2]
|
115
|
+
end
|
116
|
+
|
117
|
+
test "straight up intersection + union" do |book1, book2|
|
118
|
+
result = book1.authors.find(:mood => "happy").
|
119
|
+
union(:book_id => book1.id, :mood => "sad")
|
120
|
+
|
121
|
+
assert_equal 2, result.size
|
122
|
+
end
|
123
|
+
|
124
|
+
test "appending an empty set via union" do |book1, book2|
|
125
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
126
|
+
union(:book_id => book2.id, :mood => "sad").
|
127
|
+
union(:book_id => book2.id, :mood => "happy")
|
128
|
+
|
129
|
+
assert_equal 2, res.size
|
130
|
+
end
|
131
|
+
|
132
|
+
test "revert by applying the original intersection" do |book1, book2|
|
133
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
134
|
+
union(:book_id => book2.id, :mood => "sad").
|
135
|
+
find(:book_id => book1.id, :mood => "happy")
|
136
|
+
|
137
|
+
assert_equal 1, res.size
|
138
|
+
end
|
139
|
+
|
140
|
+
test "remove original intersection by doing diff" do |book1, book2|
|
141
|
+
res = Author.find(:book_id => book1.id, :mood => "happy").
|
142
|
+
union(:book_id => book2.id, :mood => "sad").
|
143
|
+
except(:book_id => book1.id, :mood => "happy")
|
144
|
+
|
145
|
+
assert_equal 1, res.size
|
146
|
+
assert res.map(&:mood).include?("sad")
|
147
|
+
assert res.map(&:book_id).include?(book2.id)
|
148
|
+
end
|
149
|
+
|
150
|
+
test "@myobie usecase" do |book1, book2|
|
151
|
+
res = book1.authors.find(:mood => "happy").
|
152
|
+
union(:mood => "sad", :book_id => book1.id)
|
153
|
+
|
154
|
+
assert_equal 2, res.size
|
155
|
+
end
|
87
156
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ohm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.
|
4
|
+
version: 1.0.0.rc4
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: nest
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: '1.0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '1.0'
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: scrivener
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ~>
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: 0.0.3
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.0.3
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: cutest
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ~>
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '0.1'
|
45
55
|
type: :development
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0.1'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: batch
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ~>
|
@@ -55,7 +70,12 @@ dependencies:
|
|
55
70
|
version: 0.0.1
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ~>
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.0.1
|
59
79
|
description: Ohm is a library that allows to store an object in Redis, a persistent
|
60
80
|
key-value database. It includes an extensible list of validations and has very good
|
61
81
|
performance.
|
@@ -117,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
137
|
version: 1.3.1
|
118
138
|
requirements: []
|
119
139
|
rubyforge_project: ohm
|
120
|
-
rubygems_version: 1.8.
|
140
|
+
rubygems_version: 1.8.23
|
121
141
|
signing_key:
|
122
142
|
specification_version: 3
|
123
143
|
summary: Object-hash mapping library for Redis.
|