pgrel 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b99f0c11a5e624145d178ed3bdb97dd9345ccad
4
- data.tar.gz: 7db4f0a1086111e10ded6e6f9af923179ddc2012
3
+ metadata.gz: 5a4870c7384f1433c46da0d1515243c3753e928d
4
+ data.tar.gz: 446c7ccfd60abe33f7422cd2252dfa8d9bb47281
5
5
  SHA512:
6
- metadata.gz: 4ef4b9230787c49c3ccb1b24d68113cf90b57c0ae8281e6814eb6d01c01925fc04031ee3f02e1cc09abf60386b2d3c12f6b2f46674f6b75ccc27cd34d3a08619
7
- data.tar.gz: 8aaeaab12dd78aac0ad21a49b68af1a5efb83aeb261e0cfd431f24662b5f8b0dc88dcc4b93c8115b3a0d4999ddf3ca842c32d75f92270ebab7113eac31b71acf
6
+ metadata.gz: 39575efbbe02500b4a3534e4a188b845305911e96fbd33f01a68a7eaad0fa4185261727da4a7e9bf498f506af5ee63de9a147024f5b16b283a91776ee568346c
7
+ data.tar.gz: 63c370d875d0442c14a680d846cf34c7ccdb2f03ca25d061fbe6eae2772cf5bc5b2b9dcf16febb0543ffee61ec3c00c8cfd076c0e7dcea861925d2217790a05c
@@ -11,7 +11,7 @@ before_script:
11
11
 
12
12
  matrix:
13
13
  include:
14
- - rvm: 2.2.1
14
+ - rvm: 2.2.3
15
15
  gemfile: gemfiles/rails5.gemfile
16
16
  - rvm: 2.2.1
17
17
  gemfile: gemfiles/rails42.gemfile
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/palkan/pgrel.svg?branch=master)](https://travis-ci.org/palkan/pgrel)
1
+ [![Gem Version](https://badge.fury.io/rb/pgrel.svg)](https://rubygems.org/gems/pgrel) [![Build Status](https://travis-ci.org/palkan/pgrel.svg?branch=master)](https://travis-ci.org/palkan/pgrel)
2
2
 
3
3
  ## Pgrel
4
4
 
@@ -25,10 +25,10 @@ Query by key value:
25
25
 
26
26
  ```ruby
27
27
  Hstore.where.store(:tags, a: 1, b: 2)
28
- #=> select * from hstores where tags->'a' = '1' and tags->'b' = '2'
28
+ #=> select * from hstores where tags @> '"a"=>"1","b"=>"2"'
29
29
 
30
30
  Hstore.where.store(:tags, a: [1, 2])
31
- #=> select * from hstores where tags->'a' in ('1', '2')
31
+ #=> select * from hstores where (tags @> '"a"=>"1"' or tags @> '"a"=>"2"')
32
32
  ```
33
33
 
34
34
  Keys existence:
@@ -61,14 +61,14 @@ Hstore.where.store(:tags).contained(a: 1, b: 2)
61
61
 
62
62
  All queries for Hstore also available for JSONB.
63
63
 
64
- **NOTE**. Querying by array value always resolves to `IN (...)` statement.
64
+ **NOTE**. Querying by array value always resolves to `(... or ...)` statement.
65
65
  Thus it's impossible to query json array value, e.g.:
66
66
 
67
67
  ```ruby
68
68
  Model.create!(tags: {main: ['a', 'b']})
69
69
 
70
70
  Model.where.store(:tags, main: ['a', 'b']).empty? == true
71
- #=> select * from models where tags->'main' in ('a', 'b')
71
+ #=> select * from models where (tags @> '{\"main\":\"a\"}' or tags @> '{\"main\":\"b\"}')
72
72
  ```
73
73
 
74
74
  Path query:
@@ -1,5 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rack', github: "rack/rack"
3
4
  gem 'arel', github: 'rails/arel'
4
5
  gem 'rails', github: 'rails/rails'
5
6
 
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  # data = {a: 1}
23
23
  # Model.store(:store).contains(data).all #=> [Model(name: 'first', ...)]
24
24
  def contains(opts)
25
- update_scope "#{@store_name} @> #{type_cast(opts)}"
25
+ update_scope contains_clause(opts)
26
26
  end
27
27
 
28
28
  # Whether the store is contained within provided store
@@ -52,6 +52,37 @@ module ActiveRecord
52
52
  self
53
53
  end
54
54
 
55
+ # Query by store values.
56
+ # Supports array values.
57
+ #
58
+ # NOTE: This method uses "@>" (contains) operator with logic (AND/OR)
59
+ # and not uses "->" (value-by-key). The use of "contains" operator allows us to
60
+ # use GIN index effectively.
61
+ #
62
+ # Example
63
+ # Model.create!(name: 'first', store: {b: 1, c: 2})
64
+ # Model.create!(name: 'second', store: {b: 2, c: 3})
65
+ #
66
+ # Model.store(:store, c: 2).all #=> [Model(name: 'first', ...)]
67
+ # #=> (SQL) select * from ... where store @> '"c"=>"2"'::hstore
68
+ #
69
+ # Model.store(:store, b: [1, 2]).size #=> 2
70
+ # #=> (SQL) select * from ... where (store @> '"c"=>"1"'::hstore) or
71
+ # (store @> '"c"=>"2"'::hstore)
72
+ def where(opts)
73
+ update_scope(
74
+ opts.map do |k, v|
75
+ case v
76
+ when Array
77
+ "(#{build_or_contains(k, v)})"
78
+ else
79
+ contains_clause(k => v)
80
+ end
81
+ end.join(' and ')
82
+ )
83
+ @scope
84
+ end
85
+
55
86
  if RAILS_5
56
87
  protected
57
88
 
@@ -66,20 +97,6 @@ module ActiveRecord
66
97
  @scope.table.type_cast_for_database(@store_name, value)
67
98
  )
68
99
  end
69
-
70
- def where_with_prefix(prefix, opts)
71
- where_clause = @scope.send(:where_clause_factory).build(opts, {})
72
- predicates = where_clause.ast.children.map do |rel|
73
- rel.left = to_sql_literal(prefix, rel.left)
74
- rel
75
- end
76
- where_clause = ActiveRecord::Relation::WhereClause.new(
77
- predicates,
78
- where_clause.binds
79
- )
80
- @scope.where_clause += @inverted ? where_clause.invert : where_clause
81
- @scope
82
- end
83
100
  else
84
101
  protected
85
102
 
@@ -98,15 +115,6 @@ module ActiveRecord
98
115
  )
99
116
  end
100
117
 
101
- def where_with_prefix(prefix, opts)
102
- where_value = @scope.send(:build_where, opts).map do |rel|
103
- rel.left = to_sql_literal(prefix, rel.left)
104
- @inverted ? invert_arel(rel) : rel
105
- end
106
- @scope.where_values += where_value
107
- @scope
108
- end
109
-
110
118
  def invert_arel(rel)
111
119
  case rel
112
120
  when Arel::Nodes::In
@@ -120,6 +128,16 @@ module ActiveRecord
120
128
  end
121
129
  end
122
130
  end
131
+
132
+ private
133
+
134
+ def contains_clause(opts)
135
+ "#{@store_name} @> #{type_cast(opts)}"
136
+ end
137
+
138
+ def build_or_contains(k, vals)
139
+ vals.map { |v| contains_clause(k => v) }.join(' or ')
140
+ end
123
141
  end
124
142
 
125
143
  # Base class for key-value types of stores (hstore, jsonb)
@@ -171,6 +189,31 @@ module ActiveRecord
171
189
  "#{prefix}'#{node.name}'"
172
190
  )
173
191
  end
192
+
193
+ if RAILS_5
194
+ def where_with_prefix(prefix, opts)
195
+ where_clause = @scope.send(:where_clause_factory).build(opts, {})
196
+ predicates = where_clause.ast.children.map do |rel|
197
+ rel.left = to_sql_literal(prefix, rel.left)
198
+ rel
199
+ end
200
+ where_clause = ActiveRecord::Relation::WhereClause.new(
201
+ predicates,
202
+ where_clause.binds
203
+ )
204
+ @scope.where_clause += @inverted ? where_clause.invert : where_clause
205
+ @scope
206
+ end
207
+ else
208
+ def where_with_prefix(prefix, opts)
209
+ where_value = @scope.send(:build_where, opts).map do |rel|
210
+ rel.left = to_sql_literal(prefix, rel.left)
211
+ @inverted ? invert_arel(rel) : rel
212
+ end
213
+ @scope.where_values += where_value
214
+ @scope
215
+ end
216
+ end
174
217
  end
175
218
  end
176
219
  end
@@ -2,35 +2,6 @@ module ActiveRecord
2
2
  module QueryMethods
3
3
  # Store chain for hstore columns.
4
4
  class HstoreChain < KeyStoreChain
5
- # Query by store values.
6
- #
7
- # Supports array values.
8
- #
9
- # Example
10
- # Model.create!(name: 'first', store: {b: 1, c: 2})
11
- # Model.create!(name: 'second', store: {b: 2, c: 3})
12
- #
13
- # Model.store(:store, c: 2).all #=> [Model(name: 'first', ...)]
14
- # Model.store(:store, b: [1, 2]).size #=> 2
15
- def where(opts)
16
- opts = stringify(opts)
17
- where_with_prefix "#{@store_name}->", opts
18
- end
19
-
20
- private
21
-
22
- def stringify(val)
23
- case val
24
- when String
25
- val
26
- when Array
27
- val.map { |v| stringify(v) }
28
- when Hash
29
- Hash[val.map { |k, v| [stringify(k), stringify(v)] }]
30
- else
31
- val.to_s
32
- end
33
- end
34
5
  end
35
6
  end
36
7
  end
@@ -2,23 +2,7 @@ module ActiveRecord
2
2
  module QueryMethods
3
3
  # Store chain for jsonb columns.
4
4
  class JsonbChain < KeyStoreChain
5
- # Query by store values.
6
- # Supports array values (convert to IN statement).
7
- #
8
- # Example
9
- # Model.create!(name: 'first', store: {b: 1, c: 2})
10
- # Model.create!(name: 'second', store: {b: 2, c: 3})
11
- #
12
- # Model.store(:store, c: 2).all #=> [Model(name: 'first', ...)]
13
- # Model.store(:store, b: [1, 2]).size #=> 2
14
- def where(opts)
15
- opts = flatten_json(opts)
16
- where_with_prefix "#{@store_name}->", opts
17
- end
18
-
19
- # Query by quality in path.
20
- #
21
- # Path can be set as object or as args.
5
+ # Query by value in path.
22
6
  #
23
7
  # Example:
24
8
  # Model.create!(name: 'first', store: {b: 1, c: { d: 3 } })
@@ -49,18 +33,6 @@ module ActiveRecord
49
33
 
50
34
  private
51
35
 
52
- def flatten_json(val)
53
- Hash[
54
- val.map do |k, v|
55
- if v.is_a?(Array)
56
- [k, v.map { |i| ::ActiveSupport::JSON.encode(i) }]
57
- else
58
- [k, ::ActiveSupport::JSON.encode(v)]
59
- end
60
- end
61
- ]
62
- end
63
-
64
36
  def flatten_hash(hash)
65
37
  case hash
66
38
  when Hash
@@ -1,3 +1,3 @@
1
1
  module Pgrel # :nodoc:
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -116,7 +116,8 @@ describe Hstore do
116
116
 
117
117
  context '#not' do
118
118
  it '#where' do
119
- expect(Hstore.where.store(:tags).not(a: 1, g: 'c').size).to eq 1
119
+ expect(Hstore.where.store(:tags).not(a: 2).size).to eq 4
120
+ expect(Hstore.where.store(:tags).not(a: 1, g: 'c').size).to eq 6
120
121
  end
121
122
 
122
123
  it '#any' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgrel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-30 00:00:00.000000000 Z
11
+ date: 2015-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -148,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
148
  version: '0'
149
149
  requirements: []
150
150
  rubyforge_project:
151
- rubygems_version: 2.2.2
151
+ rubygems_version: 2.4.5
152
152
  signing_key:
153
153
  specification_version: 4
154
154
  summary: ActiveRecord extension for querying hstore and jsonb.