sequel-association-filtering 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.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eebfe24a7a4bc0831e9b5d983f7d49c46f1af66b531b137cbad11d65195c02dd
|
4
|
+
data.tar.gz: 812a28ac539eab752717576a44cd63f4e95d306c9ceeb3830f044227ec6c09db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d87c8e29c99c922be6411c7f74589b3685781502a38131f7f832465a334ab62cbee3125ba0fe4c844b98d496d1c0ef49cba6091c1f96c0268a0b46bc75f665d6
|
7
|
+
data.tar.gz: 00155ca49d9f3f01d8b4cf7385f81bb8e72e0acb8ec2db436ee3ae74468cb606cab343ac52e62120a65b4f5c129eaa3cb3a1ea6212fdd9f30520372cc3173087
|
@@ -19,27 +19,47 @@ module Sequel
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module DatasetMethods
|
22
|
-
|
22
|
+
COUNT_STAR = Sequel.virtual_row{count.function.*}
|
23
|
+
|
24
|
+
def association_filter(
|
25
|
+
association_name,
|
26
|
+
invert: false,
|
27
|
+
at_least: nil,
|
28
|
+
at_most: nil,
|
29
|
+
exactly: nil
|
30
|
+
)
|
31
|
+
case [at_least, at_most, exactly].compact.length
|
32
|
+
when 0
|
33
|
+
filtering_by_count = false
|
34
|
+
when 1
|
35
|
+
filtering_by_count = true
|
36
|
+
else
|
37
|
+
raise Error, "cannot pass more than one of :at_least, :at_most, and :exactly"
|
38
|
+
end
|
39
|
+
|
23
40
|
reflection =
|
24
41
|
model.association_reflections.fetch(association_name) do
|
25
42
|
raise Error, "association #{association_name} not found on model #{model}"
|
26
43
|
end
|
27
44
|
|
28
|
-
|
29
|
-
|
30
|
-
invert ? exclude(cond) : where(cond)
|
31
|
-
else
|
32
|
-
cache_key =
|
33
|
-
_association_filter_cache_key(
|
34
|
-
reflection: reflection,
|
35
|
-
extra: :"bare_#{invert}",
|
36
|
-
)
|
45
|
+
ds = _association_filter_dataset(reflection, group_by_remote: filtering_by_count)
|
46
|
+
ds = yield(ds) if block_given?
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
48
|
+
if filtering_by_count
|
49
|
+
ds =
|
50
|
+
ds.having(
|
51
|
+
case
|
52
|
+
when at_least then COUNT_STAR >= at_least
|
53
|
+
when at_most then COUNT_STAR <= at_most
|
54
|
+
when exactly then COUNT_STAR =~ exactly
|
55
|
+
else raise Error, ""
|
56
|
+
end
|
57
|
+
)
|
42
58
|
end
|
59
|
+
|
60
|
+
cond = ds.exists
|
61
|
+
cond = Sequel.~(cond) if invert
|
62
|
+
where(cond)
|
43
63
|
end
|
44
64
|
|
45
65
|
def association_exclude(association_name, &block)
|
@@ -48,8 +68,12 @@ module Sequel
|
|
48
68
|
|
49
69
|
private
|
50
70
|
|
51
|
-
def _association_filter_dataset(reflection)
|
52
|
-
cache_key =
|
71
|
+
def _association_filter_dataset(reflection, group_by_remote:)
|
72
|
+
cache_key =
|
73
|
+
_association_filter_cache_key(
|
74
|
+
reflection: reflection,
|
75
|
+
extra: (:group_by_remote if group_by_remote)
|
76
|
+
)
|
53
77
|
|
54
78
|
ds = reflection.associated_dataset
|
55
79
|
|
@@ -71,12 +95,19 @@ module Sequel
|
|
71
95
|
local_keys = Array(local_keys)
|
72
96
|
remote_keys = Array(remote_keys)
|
73
97
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
98
|
+
result =
|
99
|
+
ds.where(
|
100
|
+
remote_keys.
|
101
|
+
zip(local_keys).
|
102
|
+
map{|r,l| {r => l}}.
|
103
|
+
inject{|a,b| Sequel.&(a, b)}
|
104
|
+
).select(1)
|
105
|
+
|
106
|
+
if group_by_remote
|
107
|
+
result.group_by(*remote_keys)
|
108
|
+
else
|
109
|
+
result
|
110
|
+
end
|
80
111
|
end
|
81
112
|
end
|
82
113
|
|
data/spec/basic_spec.rb
CHANGED
@@ -29,5 +29,14 @@ class BasicSpec < AssociationFilteringSpecs
|
|
29
29
|
|
30
30
|
assert_equal "association widgets not found on model BasicSpec::Artist", error.message
|
31
31
|
end
|
32
|
+
|
33
|
+
it "with more than one of at_least, at_most, or exactly should throw an error" do
|
34
|
+
error =
|
35
|
+
assert_raises(Sequel::Plugins::AssociationFiltering::Error) do
|
36
|
+
Artist.association_filter(:widgets, at_most: 4, exactly: 5)
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal "cannot pass more than one of :at_least, :at_most, and :exactly", error.message
|
40
|
+
end
|
32
41
|
end
|
33
42
|
end
|
data/spec/one_to_many_spec.rb
CHANGED
@@ -58,6 +58,25 @@ class OneToManySpec < AssociationFilteringSpecs
|
|
58
58
|
Album.where(artist_id: 5).delete
|
59
59
|
assert_equal 9, ds.count
|
60
60
|
end
|
61
|
+
|
62
|
+
it "should support at_least/exactly/at_most args" do
|
63
|
+
Album.create(artist_id: 1)
|
64
|
+
|
65
|
+
ds = Artist.association_filter(:albums, exactly: 11)
|
66
|
+
assert_equal 1, ds.count
|
67
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE ("albums"."artist_id" = "artists"."id") GROUP BY "albums"."artist_id" HAVING (count(*) = 11)))), ds.sql
|
68
|
+
assert_equal [1], ds.select_map(:id)
|
69
|
+
|
70
|
+
ds = Artist.association_filter(:albums, at_least: 2){|albums| albums.where{id > 90}}
|
71
|
+
assert_equal 1, ds.count
|
72
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."artist_id" = "artists"."id") AND ("id" > 90)) GROUP BY "albums"."artist_id" HAVING (count(*) >= 2)))), ds.sql
|
73
|
+
assert_equal [1], ds.select_map(:id)
|
74
|
+
|
75
|
+
ds = Artist.association_filter(:albums, at_most: 1){|albums| albums.where{id > 90}}
|
76
|
+
assert_equal 9, ds.count
|
77
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."artist_id" = "artists"."id") AND ("id" > 90)) GROUP BY "albums"."artist_id" HAVING (count(*) <= 1)))), ds.sql
|
78
|
+
assert_equal (2..10).to_a, ds.select_order_map(:id)
|
79
|
+
end
|
61
80
|
end
|
62
81
|
end
|
63
82
|
|
@@ -130,6 +149,25 @@ class OneToManySpec < AssociationFilteringSpecs
|
|
130
149
|
Album.where(id_1: 5, id_2: 6).delete
|
131
150
|
assert_equal 99, ds.count
|
132
151
|
end
|
152
|
+
|
153
|
+
it "should support at_least/exactly/at_most args" do
|
154
|
+
Album.dataset.insert(id_1: 1, id_2: 1, id_3: 11)
|
155
|
+
|
156
|
+
ds = Artist.association_filter(:albums, exactly: 11)
|
157
|
+
assert_equal 1, ds.count
|
158
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."id_1" = "artists"."id_1") AND ("albums"."id_2" = "artists"."id_2")) GROUP BY "albums"."id_1", "albums"."id_2" HAVING (count(*) = 11)))), ds.sql
|
159
|
+
assert_equal [[1, 1]], ds.select_map([:id_1, :id_2])
|
160
|
+
|
161
|
+
ds = Artist.association_filter(:albums, at_least: 2){|albums| albums.where{serial_column > 900}}
|
162
|
+
assert_equal 1, ds.count
|
163
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."id_1" = "artists"."id_1") AND ("albums"."id_2" = "artists"."id_2") AND ("serial_column" > 900)) GROUP BY "albums"."id_1", "albums"."id_2" HAVING (count(*) >= 2)))), ds.sql
|
164
|
+
assert_equal [[1, 1]], ds.select_map([:id_1, :id_2])
|
165
|
+
|
166
|
+
ds = Artist.association_filter(:albums, at_most: 1){|albums| albums.where{serial_column > 900}}
|
167
|
+
assert_equal 99, ds.count
|
168
|
+
assert_equal %(SELECT * FROM "artists" WHERE (EXISTS (SELECT 1 FROM "albums" WHERE (("albums"."id_1" = "artists"."id_1") AND ("albums"."id_2" = "artists"."id_2") AND ("serial_column" > 900)) GROUP BY "albums"."id_1", "albums"."id_2" HAVING (count(*) <= 1)))), ds.sql
|
169
|
+
assert_equal (2..100).to_a, ds.select_order_map(:serial_column)
|
170
|
+
end
|
133
171
|
end
|
134
172
|
|
135
173
|
describe "association_exclude through a one_to_many association" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-association-filtering
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hanks
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-05-
|
11
|
+
date: 2018-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|