Commit 3f692b06bde751e0c9cf15710f0de932b22f295e
1 parent
d5312b89
Exists in
master
and in
1 other branch
Added where and rewhere methods on relation
Showing
3 changed files
with
107 additions
and
0 deletions
Show diff stats
lib/searchkick/relation.rb
@@ -89,6 +89,34 @@ module Searchkick | @@ -89,6 +89,34 @@ module Searchkick | ||
89 | end | 89 | end |
90 | 90 | ||
91 | # experimental | 91 | # experimental |
92 | + def where(value) | ||
93 | + clone.where!(value) | ||
94 | + end | ||
95 | + | ||
96 | + # experimental | ||
97 | + def where!(value) | ||
98 | + check_loaded | ||
99 | + if @options[:where] | ||
100 | + @options[:where] = {_and: [@options[:where], ensure_permitted(value)]} | ||
101 | + else | ||
102 | + @options[:where] = ensure_permitted(value) | ||
103 | + end | ||
104 | + self | ||
105 | + end | ||
106 | + | ||
107 | + # experimental | ||
108 | + def rewhere(value) | ||
109 | + clone.rewhere!(value) | ||
110 | + end | ||
111 | + | ||
112 | + # experimental | ||
113 | + def rewhere!(value) | ||
114 | + check_loaded | ||
115 | + @options[:where] = ensure_permitted(value) | ||
116 | + self | ||
117 | + end | ||
118 | + | ||
119 | + # experimental | ||
92 | def only(*keys) | 120 | def only(*keys) |
93 | Relation.new(@model, @term, **@options.slice(*keys)) | 121 | Relation.new(@model, @term, **@options.slice(*keys)) |
94 | end | 122 | end |
@@ -118,5 +146,11 @@ module Searchkick | @@ -118,5 +146,11 @@ module Searchkick | ||
118 | # reset query since options will change | 146 | # reset query since options will change |
119 | @query = nil | 147 | @query = nil |
120 | end | 148 | end |
149 | + | ||
150 | + # provides *very* basic protection from unfiltered parameters | ||
151 | + # this is not meant to be comprehensive and may be expanded in the future | ||
152 | + def ensure_permitted(obj) | ||
153 | + obj.to_h | ||
154 | + end | ||
121 | end | 155 | end |
122 | end | 156 | end |
test/parameters_test.rb
@@ -20,18 +20,56 @@ class ParametersTest < Minitest::Test | @@ -20,18 +20,56 @@ class ParametersTest < Minitest::Test | ||
20 | end | 20 | end |
21 | end | 21 | end |
22 | 22 | ||
23 | + def test_where_relation | ||
24 | + params = ActionController::Parameters.new({store_id: 1}) | ||
25 | + assert_raises(ActionController::UnfilteredParameters) do | ||
26 | + Product.search("*").where(params) | ||
27 | + end | ||
28 | + end | ||
29 | + | ||
30 | + def test_rewhere_relation | ||
31 | + params = ActionController::Parameters.new({store_id: 1}) | ||
32 | + assert_raises(ActionController::UnfilteredParameters) do | ||
33 | + Product.search("*").where(params) | ||
34 | + end | ||
35 | + end | ||
36 | + | ||
23 | def test_where_permitted | 37 | def test_where_permitted |
24 | store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | 38 | store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] |
25 | params = ActionController::Parameters.new({store_id: 1}) | 39 | params = ActionController::Parameters.new({store_id: 1}) |
26 | assert_search "product", ["Product A"], where: params.permit(:store_id) | 40 | assert_search "product", ["Product A"], where: params.permit(:store_id) |
27 | end | 41 | end |
28 | 42 | ||
43 | + def test_where_permitted_relation | ||
44 | + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | ||
45 | + params = ActionController::Parameters.new({store_id: 1}) | ||
46 | + assert_equal ["Product A"], Product.search("product").where(params.permit(:store_id)).map(&:name) | ||
47 | + end | ||
48 | + | ||
49 | + def test_rewhere_permitted_relation | ||
50 | + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | ||
51 | + params = ActionController::Parameters.new({store_id: 1}) | ||
52 | + assert_equal ["Product A"], Product.search("product").rewhere(params.permit(:store_id)).map(&:name) | ||
53 | + end | ||
54 | + | ||
29 | def test_where_value | 55 | def test_where_value |
30 | store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | 56 | store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] |
31 | params = ActionController::Parameters.new({store_id: 1}) | 57 | params = ActionController::Parameters.new({store_id: 1}) |
32 | assert_search "product", ["Product A"], where: {store_id: params[:store_id]} | 58 | assert_search "product", ["Product A"], where: {store_id: params[:store_id]} |
33 | end | 59 | end |
34 | 60 | ||
61 | + def test_where_value_relation | ||
62 | + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | ||
63 | + params = ActionController::Parameters.new({store_id: 1}) | ||
64 | + assert_equal ["Product A"], Product.search("product").where(store_id: params[:store_id]).map(&:name) | ||
65 | + end | ||
66 | + | ||
67 | + def test_rewhere_value_relation | ||
68 | + store [{name: "Product A", store_id: 1}, {name: "Product B", store_id: 2}] | ||
69 | + params = ActionController::Parameters.new({store_id: 1}) | ||
70 | + assert_equal ["Product A"], Product.search("product").where(store_id: params[:store_id]).map(&:name) | ||
71 | + end | ||
72 | + | ||
35 | def test_where_hash | 73 | def test_where_hash |
36 | params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) | 74 | params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) |
37 | error = assert_raises(TypeError) do | 75 | error = assert_raises(TypeError) do |
@@ -40,6 +78,24 @@ class ParametersTest < Minitest::Test | @@ -40,6 +78,24 @@ class ParametersTest < Minitest::Test | ||
40 | assert_equal error.message, "can't cast ActionController::Parameters" | 78 | assert_equal error.message, "can't cast ActionController::Parameters" |
41 | end | 79 | end |
42 | 80 | ||
81 | + # TODO raise error without to_a | ||
82 | + def test_where_hash_relation | ||
83 | + params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) | ||
84 | + error = assert_raises(TypeError) do | ||
85 | + Product.search("product").where(store_id: params[:store_id]).to_a | ||
86 | + end | ||
87 | + assert_equal error.message, "can't cast ActionController::Parameters" | ||
88 | + end | ||
89 | + | ||
90 | + # TODO raise error without to_a | ||
91 | + def test_rewhere_hash_relation | ||
92 | + params = ActionController::Parameters.new({store_id: {value: 10, boost: 2}}) | ||
93 | + error = assert_raises(TypeError) do | ||
94 | + Product.search("product").rewhere(store_id: params[:store_id]).to_a | ||
95 | + end | ||
96 | + assert_equal error.message, "can't cast ActionController::Parameters" | ||
97 | + end | ||
98 | + | ||
43 | def test_aggs_where | 99 | def test_aggs_where |
44 | params = ActionController::Parameters.new({store_id: 1}) | 100 | params = ActionController::Parameters.new({store_id: 1}) |
45 | assert_raises(ActionController::UnfilteredParameters) do | 101 | assert_raises(ActionController::UnfilteredParameters) do |
test/where_test.rb
@@ -82,6 +82,23 @@ class WhereTest < Minitest::Test | @@ -82,6 +82,23 @@ class WhereTest < Minitest::Test | ||
82 | assert_search "product", ["Product B"], where: {user_ids: {_not: [3, nil]}} | 82 | assert_search "product", ["Product B"], where: {user_ids: {_not: [3, nil]}} |
83 | end | 83 | end |
84 | 84 | ||
85 | + def test_where_relation | ||
86 | + now = Time.now | ||
87 | + store [ | ||
88 | + {name: "Product A", store_id: 1, in_stock: true, backordered: true, created_at: now, orders_count: 4, user_ids: [1, 2, 3]}, | ||
89 | + {name: "Product B", store_id: 2, in_stock: true, backordered: false, created_at: now - 1, orders_count: 3, user_ids: [1]}, | ||
90 | + {name: "Product C", store_id: 3, in_stock: false, backordered: true, created_at: now - 2, orders_count: 2, user_ids: [1, 3]}, | ||
91 | + {name: "Product D", store_id: 4, in_stock: false, backordered: false, created_at: now - 3, orders_count: 1} | ||
92 | + ] | ||
93 | + assert_equal ["Product A", "Product B"], Product.search("product").where(in_stock: true).map(&:name) | ||
94 | + | ||
95 | + # multiple where | ||
96 | + assert_equal ["Product A"], Product.search("product").where(in_stock: true).where(backordered: true).map(&:name) | ||
97 | + | ||
98 | + # rewhere | ||
99 | + assert_equal ["Product A", "Product C"], Product.search("product").where(in_stock: true).rewhere(backordered: true).map(&:name) | ||
100 | + end | ||
101 | + | ||
85 | def test_where_string_operators | 102 | def test_where_string_operators |
86 | error = assert_raises(ArgumentError) do | 103 | error = assert_raises(ArgumentError) do |
87 | assert_search "product", [], where: {store_id: {"lt" => 2}} | 104 | assert_search "product", [], where: {store_id: {"lt" => 2}} |